From: Mark Andrews Date: Mon, 5 Sep 2005 00:12:29 +0000 (+0000) Subject: 1913. [func] Integrate contibuted DLZ code into named. [RT #11382] X-Git-Tag: v9.4.0a1~78^2 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=03e200df5dc283f24a6a349f0b31d3eab26da893;p=thirdparty%2Fbind9.git 1913. [func] Integrate contibuted DLZ code into named. [RT #11382] --- diff --git a/CHANGES b/CHANGES index e550400708c..89f5c6a6d5f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +1913. [func] Integrate contibuted DLZ code into named. [RT #11382] + 1912. [port] aix: atomic locking for powerpc. [RT #15020] 1911. [bug] Update windows socket code. [RT #14965] diff --git a/bin/check/check-tool.c b/bin/check/check-tool.c index 60dad979d76..bbadf135bd3 100644 --- a/bin/check/check-tool.c +++ b/bin/check/check-tool.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check-tool.c,v 1.19 2005/08/24 23:53:54 marka Exp $ */ +/* $Id: check-tool.c,v 1.20 2005/09/05 00:10:51 marka Exp $ */ /*! \file */ @@ -435,7 +435,7 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename, dns_zone_setchecksrv(zone, checksrv); CHECK(dns_zone_load(zone)); - if (zonep != NULL){ + if (zonep != NULL) { *zonep = zone; zone = NULL; } diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in index 52e9cf8d651..37e1b12c49e 100644 --- a/bin/named/Makefile.in +++ b/bin/named/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.86 2005/01/12 01:56:06 marka Exp $ +# $Id: Makefile.in,v 1.87 2005/09/05 00:10:51 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -31,12 +31,20 @@ DBDRIVER_SRCS = DBDRIVER_INCLUDES = DBDRIVER_LIBS = +DLZ_DRIVER_DIR = ${top_srcdir}/contrib/dlz/drivers + +DLZDRIVER_OBJS = @DLZ_DRIVER_OBJS@ +DLZDRIVER_SRCS = @DLZ_DRIVER_SRCS@ +DLZDRIVER_INCLUDES = @DLZ_DRIVER_INCLUDES@ +DLZDRIVER_LIBS = @DLZ_DRIVER_LIBS@ + CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include \ ${LWRES_INCLUDES} ${DNS_INCLUDES} ${BIND9_INCLUDES} \ ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \ - ${DBDRIVER_INCLUDES} + ${DLZDRIVER_INCLUDES} ${DBDRIVER_INCLUDES} + +CDEFINES = @USE_DLZ@ -CDEFINES = CWARNINGS = DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@ @@ -57,7 +65,8 @@ DEPLIBS = ${LWRESDEPLIBS} ${DNSDEPLIBS} ${BIND9DEPLIBS} \ ${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${ISCDEPLIBS} LIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} \ - ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} ${DBDRIVER_LIBS} @LIBS@ + ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} \ + ${DLZDRIVER_LIBS} ${DBDRIVER_LIBS} @LIBS@ SUBDIRS = unix @@ -71,7 +80,7 @@ OBJS = builtin.@O@ client.@O@ config.@O@ control.@O@ \ zoneconf.@O@ \ lwaddr.@O@ lwresd.@O@ lwdclient.@O@ lwderror.@O@ lwdgabn.@O@ \ lwdgnba.@O@ lwdgrbn.@O@ lwdnoop.@O@ lwsearch.@O@ \ - $(DBDRIVER_OBJS) + ${DLZDRIVER_OBJS} ${DBDRIVER_OBJS} UOBJS = unix/os.@O@ @@ -83,7 +92,7 @@ SRCS = builtin.c client.c config.c control.c \ zoneconf.c \ lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \ lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c \ - $(DBDRIVER_SRCS) + ${DLZDRIVER_SRCS} ${DBDRIVER_SRCS} MANPAGES = named.8 lwresd.8 named.conf.5 @@ -133,3 +142,4 @@ install:: named@EXEEXT@ lwresd@EXEEXT@ installdirs ${INSTALL_DATA} ${srcdir}/lwresd.8 ${DESTDIR}${mandir}/man8 ${INSTALL_DATA} ${srcdir}/named.conf.5 ${DESTDIR}${mandir}/man5 +@DLZ_DRIVER_RULES@ diff --git a/bin/named/main.c b/bin/named/main.c index f40a8c6324d..2d14bbb68af 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: main.c,v 1.147 2005/06/10 07:00:19 marka Exp $ */ +/* $Id: main.c,v 1.148 2005/09/05 00:10:52 marka Exp $ */ /*! \file */ @@ -73,6 +73,13 @@ */ /* #include "xxdb.h" */ +/* + * Include DLZ drivers if appropriate. + */ +#ifdef DLZ +#include +#endif + static isc_boolean_t want_stats = ISC_FALSE; static char program_name[ISC_DIR_NAMEMAX] = "named"; static char absolute_conffile[ISC_DIR_PATHMAX]; @@ -675,6 +682,16 @@ setup(void) { */ /* xxdb_init(); */ +#ifdef DLZ + /* + * Registyer any DLZ drivers. + */ + result = dlz_drivers_init(); + if (result != ISC_R_SUCCESS) + ns_main_earlyfatal("dlz_drivers_init() failed: %s", + isc_result_totext(result)); +#endif + ns_server_create(ns_g_mctx, &ns_g_server); } @@ -691,6 +708,13 @@ cleanup(void) { */ /* xxdb_clear(); */ +#ifdef DLZ + /* + * Unregister any DLZ drivers. + */ + dlz_drivers_clear(); +#endif + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN, ISC_LOG_NOTICE, "exiting"); ns_log_shutdown(); diff --git a/bin/named/query.c b/bin/named/query.c index 285f7133fad..8cd4237e8d8 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.272 2005/08/18 00:57:26 marka Exp $ */ +/* $Id: query.c,v 1.273 2005/09/05 00:10:52 marka Exp $ */ /*! \file */ @@ -29,6 +29,9 @@ #include #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -508,7 +511,7 @@ ns_query_init(ns_client_t *client) { client->query.authdb = NULL; client->query.authzone = NULL; client->query.authdbset = ISC_FALSE; - client->query.isreferral = ISC_FALSE; + client->query.isreferral = ISC_FALSE; query_reset(client, ISC_FALSE); result = query_newdbversion(client, 3); if (result != ISC_R_SUCCESS) { @@ -577,7 +580,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name, /* * This limits our searching to the zone where the first name * (the query target) was looked for. This prevents following - * CNAMES or DNAMES into other zones and prevents returning + * CNAMES or DNAMES into other zones and prevents returning * additional data from other zones. */ if (!client->view->additionalfromauth && @@ -654,7 +657,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name, ISC_LOG_DEBUG(3), "%s approved", msg); } - } else { + } else { ns_client_aclmsg("query", name, qtype, client->view->rdclass, msg, sizeof(msg)); @@ -733,7 +736,7 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) result = dns_zone_getdb(zone, &db); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) goto fail; result = query_validatezonedb(client, name, qtype, options, zone, db, @@ -801,7 +804,7 @@ query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, if (check_acl) { isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0); char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")]; - + result = ns_client_checkaclsilent(client, client->view->queryacl, ISC_TRUE); @@ -867,9 +870,85 @@ query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, { isc_result_t result; +#ifdef DLZ + isc_result_t tresult; + unsigned int namelabels; + unsigned int zonelabels; + dns_zone_t *zone = NULL; + dns_db_t *tdbp; + + REQUIRE(zonep != NULL && *zonep == NULL); + + tdbp = NULL; + + /* Calculate how many labels are in name. */ + namelabels = dns_name_countlabels(name); + zonelabels = 0; + + /* Try to find name in bind's standard database. */ + result = query_getzonedb(client, name, qtype, options, &zone, + dbp, versionp); + + /* See how many labels are in the zone's name. */ + if (result == ISC_R_SUCCESS && zone != NULL) + zonelabels = dns_name_countlabels(dns_zone_getorigin(zone)); + /* + * If # zone labels < # name labels, try to find an even better match + * Only try if a DLZ driver is loaded for this view + */ + if (zonelabels < namelabels && client->view->dlzdatabase != NULL) { + tresult = dns_dlzfindzone(client->view, name, + zonelabels, &tdbp); + /* If we successful, we found a better match. */ + if (tresult == ISC_R_SUCCESS) { + /* + * If the previous search returned a zone, detach it. + */ + if (zone != NULL) + dns_zone_detach(&zone); + + /* + * If the previous search returned a database, + * detach it. + */ + if (*dbp != NULL) + dns_db_detach(dbp); + + /* + * If the previous search returned a version, clear it. + */ + *versionp = NULL; + + /* + * Get our database version. + */ + dns_db_currentversion(tdbp, versionp); + + /* + * Be sure to return our database. + */ + *dbp = tdbp; + + /* + * We return a null zone, No stats for DLZ zones. + */ + zone = NULL; + result = tresult; + } + } +#else result = query_getzonedb(client, name, qtype, options, zonep, dbp, versionp); +#endif + + /* If successfull, Transfer ownership of zone. */ if (result == ISC_R_SUCCESS) { +#ifdef DLZ + *zonep = zone; +#endif + /* + * If neither attempt above succeeded, return the cache instead + */ *is_zonep = ISC_TRUE; } else if (result == ISC_R_NOTFOUND) { result = query_getcachedb(client, name, qtype, dbp, options); @@ -1032,7 +1111,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { */ goto try_glue; - result = dns_db_find(db, name, version, type, client->query.dboptions, + result = dns_db_find(db, name, version, type, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); if (result == ISC_R_SUCCESS) @@ -1248,7 +1327,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { * recursing to add address records, which in turn can cause * recursion to add KEYs. */ - if (type == dns_rdatatype_srv && trdataset != NULL) { + if (type == dns_rdatatype_srv && trdataset != NULL) { /* * If we're adding SRV records to the additional data * section, it's helpful if we add the SRV additional data @@ -1325,7 +1404,7 @@ query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0, dns_db_closeversion(db_current, &version_current, ISC_FALSE); if (db0 == NULL && db_current != NULL) dns_db_detach(&db_current); - + return (result); } @@ -1391,7 +1470,7 @@ query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { * lookup and iterate over the node. * XXXJT: this approach can cause a suboptimal result when the cache * DB only has partial address types and the glue DB has remaining - * ones. + * ones. */ type = dns_rdatatype_any; @@ -1682,7 +1761,7 @@ query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { setcache: /* * Set the new result in the cache if required. We do not support - * caching additional data from a cache DB. + * caching additional data from a cache DB. */ if (needadditionalcache == ISC_TRUE && (additionaltype == dns_rdatasetadditional_fromauth || @@ -2429,20 +2508,20 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db, * Given: * example SOA * example NSEC b.example - * b.example A - * b.example NSEC a.d.example - * a.d.example A - * a.d.example NSEC g.f.example - * g.f.example A - * g.f.example NSEC z.i.example - * z.i.example A - * z.i.example NSEC example + * b.example A + * b.example NSEC a.d.example + * a.d.example A + * a.d.example NSEC g.f.example + * g.f.example A + * g.f.example NSEC z.i.example + * z.i.example A + * z.i.example NSEC example * * QNAME: * a.example -> example NSEC b.example - * owner common example - * next common example - * wild *.example + * owner common example + * next common example + * wild *.example * d.b.example -> b.example NSEC a.d.example * owner common b.example * next common example @@ -2453,7 +2532,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db, * wild *.f.example * j.example -> z.i.example NSEC example * owner common example - * next common example + * next common example * wild *.f.example */ options = client->query.dboptions | DNS_DBFIND_NOWILD; @@ -2514,7 +2593,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db, name = wname; goto again; } - } + } cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); @@ -2777,7 +2856,7 @@ static isc_result_t rdata_tonetaddr(dns_rdata_t *rdata, isc_netaddr_t *netaddr) { struct in_addr ina; struct in6_addr in6a; - + switch (rdata->type) { case dns_rdatatype_a: INSIST(rdata->length == 4); @@ -2830,7 +2909,7 @@ setup_query_sortlist(ns_client_t *client) { isc_netaddr_t netaddr; dns_rdatasetorderfunc_t order = NULL; void *order_arg = NULL; - + isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); switch (ns_sortlist_setup(client->view->sortlist, &netaddr, &order_arg)) { @@ -3121,7 +3200,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) goto resume; } - + /* * Not returning from recursion. */ @@ -3216,7 +3295,17 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) if (event == NULL && client->query.restarts == 0) { if (is_zone) { - dns_zone_attach(zone, &client->query.authzone); +#ifdef DLZ + if (zone != NULL) { + /* + * if is_zone = true, zone = NULL then this is + * a DLZ zone. Don't attempt to attach zone. + */ +#endif + dns_zone_attach(zone, &client->query.authzone); +#ifdef DLZ + } +#endif dns_db_attach(db, &client->query.authdb); } client->query.authdbset = ISC_TRUE; @@ -4131,10 +4220,10 @@ ns_query_start(ns_client_t *client) { if ((message->flags & DNS_MESSAGEFLAG_RD) != 0) client->query.attributes |= NS_QUERYATTR_WANTRECURSION; - + if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0) client->attributes |= NS_CLIENTATTR_WANTDNSSEC; - + if (client->view->minimalresponses) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); diff --git a/bin/named/server.c b/bin/named/server.c index 1a1674f517a..a169d67268b 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.449 2005/08/23 02:36:07 marka Exp $ */ +/* $Id: server.c,v 1.450 2005/09/05 00:10:52 marka Exp $ */ /*! \file */ @@ -48,6 +48,9 @@ #include #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -826,6 +829,11 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, cfg_obj_t *forwarders; cfg_obj_t *alternates; cfg_obj_t *zonelist; +#ifdef DLZ + cfg_obj_t *dlz; + unsigned int dlzargc; + char **dlzargv; +#endif cfg_obj_t *disabled; cfg_obj_t *obj; cfg_listelt_t *element; @@ -954,6 +962,45 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, actx)); } +#ifdef DLZ + /* + * Create Dynamically Loadable Zone driver. + */ + dlz = NULL; + if (voptions != NULL) + (void)cfg_map_get(voptions, "dlz", &dlz); + else + (void)cfg_map_get(config, "dlz", &dlz); + + obj = NULL; + if (dlz != NULL) { + (void)cfg_map_get(cfg_tuple_get(dlz, "options"), + "database", &obj); + if (obj != NULL) { + char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj)); + if (s == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv); + if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, s); + goto cleanup; + } + + obj = cfg_tuple_get(dlz, "name"); + result = dns_dlzcreate(mctx, cfg_obj_asstring(obj), + dlzargv[0], dlzargc, dlzargv, + &view->dlzdatabase); + isc_mem_free(mctx, s); + isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv)); + if (result == ISC_R_SUCCESS) + goto cleanup; + } + } +#endif + /* * Configure the view's cache. Try to reuse an existing * cache if possible, otherwise create a new cache. diff --git a/bin/named/xfrout.c b/bin/named/xfrout.c index 3b8e19d5cf7..adce9985c8c 100644 --- a/bin/named/xfrout.c +++ b/bin/named/xfrout.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: xfrout.c,v 1.119 2005/04/27 04:55:55 sra Exp $ */ +/* $Id: xfrout.c,v 1.120 2005/09/05 00:10:53 marka Exp $ */ #include @@ -27,6 +27,9 @@ #include #include +#ifdef DLZ +#include +#endif #include #include #include @@ -905,6 +908,9 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; char keyname[DNS_NAME_FORMATSIZE]; isc_boolean_t is_poll = ISC_FALSE; +#ifdef DLZ + isc_boolean_t is_dlz = ISC_FALSE; +#endif switch (reqtype) { case dns_rdatatype_axfr: @@ -955,19 +961,71 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, &zone); + if (result != ISC_R_SUCCESS) - FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", - question_name, question_class); - switch(dns_zone_gettype(zone)) { - case dns_zone_master: - case dns_zone_slave: - break; /* Master and slave zones are OK for transfer. */ - default: - FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", - question_name, question_class); +#ifdef DLZ + { + /* + * Normal zone table does not have a match. Try the DLZ database + */ + if (client->view->dlzdatabase != NULL) { + result = dns_dlzallowzonexfr(client->view, + question_name, &client->peeraddr, + &db); + + if (result == ISC_R_NOPERM) { + char _buf1[DNS_NAME_FORMATSIZE]; + char _buf2[DNS_RDATACLASS_FORMATSIZE]; + + result = DNS_R_REFUSED; + dns_name_format(question_name, _buf1, + sizeof(_buf1)); + dns_rdataclass_format(question_class, + _buf2, sizeof(_buf2)); + ns_client_log(client, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_XFER_OUT, + ISC_LOG_ERROR, + "zone transfer '%s/%s' denied", + _buf1, _buf2); + goto failure; + } + if (result != ISC_R_SUCCESS) +#endif + FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", + question_name, question_class); +#ifdef DLZ + is_dlz = ISC_TRUE; + /* + * DLZ only support full zone transfer, not incremental + */ + if (reqtype != dns_rdatatype_axfr) { + mnemonic = "AXFR-style IXFR"; + reqtype = dns_rdatatype_axfr; + } + + } else { + /* + * not DLZ and not in normal zone table, we are + * not authoritative + */ + FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", + question_name, question_class); + } + } else { + /* zone table has a match */ +#endif + switch(dns_zone_gettype(zone)) { + case dns_zone_master: + case dns_zone_slave: + break; /* Master and slave zones are OK for transfer. */ + default: + FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); + } + CHECK(dns_zone_getdb(zone, &db)); + dns_db_currentversion(db, &ver); +#ifdef DLZ } - CHECK(dns_zone_getdb(zone, &db)); - dns_db_currentversion(db, &ver); +#endif xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s question section OK", mnemonic); @@ -1023,11 +1081,20 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { /* * Decide whether to allow this transfer. */ - ns_client_aclmsg("zone transfer", question_name, reqtype, - client->view->rdclass, msg, sizeof(msg)); - CHECK(ns_client_checkacl(client, msg, - dns_zone_getxfracl(zone), ISC_TRUE, - ISC_LOG_ERROR)); +#ifdef DLZ + /* + * if not a DLZ zone decide whether to allow this transfer. + */ + if (!is_dlz) { +#endif + ns_client_aclmsg("zone transfer", question_name, reqtype, + client->view->rdclass, msg, sizeof(msg)); + CHECK(ns_client_checkacl(client, msg, + dns_zone_getxfracl(zone), ISC_TRUE, + ISC_LOG_ERROR)); +#ifdef DLZ + } +#endif /* * AXFR over UDP is not possible. @@ -1051,6 +1118,10 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { /* * Get a dynamically allocated copy of the current SOA. */ +#ifdef DLZ + if (is_dlz) + dns_db_currentversion(db, &ver); +#endif CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, ¤t_soa_tuple)); @@ -1133,15 +1204,32 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { * Create the xfrout context object. This transfers the ownership * of "stream", "db", "ver", and "quota" to the xfrout context object. */ - CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, - reqtype, question_class, db, ver, quota, - stream, dns_message_gettsigkey(request), - tsigbuf, - dns_zone_getmaxxfrout(zone), - dns_zone_getidleout(zone), - (format == dns_many_answers) ? - ISC_TRUE : ISC_FALSE, - &xfr)); + + + +#ifdef DLZ + if (is_dlz) + CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, + reqtype, question_class, db, ver, quota, + stream, dns_message_gettsigkey(request), + tsigbuf, + 3600, + 3600, + (format == dns_many_answers) ? + ISC_TRUE : ISC_FALSE, + &xfr)); + else +#endif + CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, + reqtype, question_class, db, ver, quota, + stream, dns_message_gettsigkey(request), + tsigbuf, + dns_zone_getmaxxfrout(zone), + dns_zone_getidleout(zone), + (format == dns_many_answers) ? + ISC_TRUE : ISC_FALSE, + &xfr)); + xfr->mnemonic = mnemonic; stream = NULL; quota = NULL; diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index a13fe7503b3..838b0072f03 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: conf.sh.in,v 1.32 2005/07/27 04:12:00 marka Exp $ +# $Id: conf.sh.in,v 1.33 2005/09/05 00:10:53 marka Exp $ # # Common configuration data for system tests, to be sourced into @@ -43,8 +43,8 @@ CHECKCONF=$TOP/bin/check/named-checkconf # The "stress" test is not run by default since it creates enough # load on the machine to make it unusable to other users. # v6synth -SUBDIRS="cacheclean checkconf checknames dnssec forward glue ixfr limits \ - lwresd masterfile masterformat notify nsupdate resolver sortlist stub \ +SUBDIRS="cacheclean checkconf checknames dnssec forward glue ixfr limits + lwresd masterfile masterformat notify nsupdate resolver sortlist stub tkey unknown upforwd views xfer xferquota zonechecks" # PERL will be an empty string if no perl interpreter was found. diff --git a/configure b/configure index ade2f474e37..946a3726925 100755 --- a/configure +++ b/configure @@ -14,7 +14,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # -# $Id: configure,v 1.380 2005/09/01 03:36:18 marka Exp $ +# $Id: configure,v 1.381 2005/09/05 00:12:29 marka Exp $ # # Portions Copyright (C) 1996-2001 Nominum, Inc. # @@ -29,7 +29,7 @@ # 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. -# From configure.in Revision: 1.391 . +# From configure.in Revision: 1.392 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # @@ -495,8 +495,8 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS subdirs build build_cpu build_vendor build_os host host_cpu host_vendor host_os SET_MAKE RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S STD_CINCLUDES STD_CDEFINES STD_CWARNINGS CCOPT AR ARFLAGS LN ETAGS PERL CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP ISC_SOCKADDR_LEN_T ISC_PLATFORM_HAVELONGLONG ISC_PLATFORM_HAVELIFCONF ISC_PLATFORM_NEEDSYSSELECTH LWRES_PLATFORM_NEEDSYSSELECTH USE_OPENSSL DST_OPENSSL_INC USE_GSSAPI DST_GSSAPI_INC DNS_CRYPTO_LIBS ALWAYS_DEFINES ISC_PLATFORM_USETHREADS ISC_THREAD_DIR MKDEPCC MKDEPCFLAGS MKDEPPROG IRIX_DNSSEC_WARNINGS_HACK purify_path PURIFY ECHO ac_ct_AR STRIP ac_ct_STRIP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL O A SA LIBTOOL_MKDEP_SED LIBTOOL_MODE_COMPILE LIBTOOL_MODE_INSTALL LIBTOOL_MODE_LINK LIBTOOL_ALLOW_UNDEFINED LIBTOOL_IN_MAIN LIBBIND ISC_PLATFORM_HAVEIPV6 LWRES_PLATFORM_HAVEIPV6 ISC_PLATFORM_NEEDNETINETIN6H LWRES_PLATFORM_NEEDNETINETIN6H ISC_PLATFORM_NEEDNETINET6IN6H LWRES_PLATFORM_NEEDNETINET6IN6H ISC_PLATFORM_HAVEINADDR6 LWRES_PLATFORM_HAVEINADDR6 ISC_PLATFORM_NEEDIN6ADDRANY LWRES_PLATFORM_NEEDIN6ADDRANY ISC_PLATFORM_NEEDIN6ADDRLOOPBACK LWRES_PLATFORM_NEEDIN6ADDRLOOPBACK ISC_PLATFORM_HAVEIN6PKTINFO ISC_PLATFORM_FIXIN6ISADDR ISC_IPV6_H ISC_IPV6_O ISC_ISCIPV6_O ISC_IPV6_C LWRES_HAVE_SIN6_SCOPE_ID ISC_PLATFORM_HAVESCOPEID ISC_PLATFORM_HAVEIF_LADDRREQ ISC_PLATFORM_HAVEIF_LADDRCONF ISC_PLATFORM_NEEDNTOP ISC_PLATFORM_NEEDPTON ISC_PLATFORM_NEEDATON ISC_PLATFORM_HAVESALEN LWRES_PLATFORM_HAVESALEN ISC_PLATFORM_MSGHDRFLAVOR ISC_PLATFORM_NEEDPORTT ISC_LWRES_NEEDADDRINFO ISC_LWRES_NEEDRRSETINFO ISC_LWRES_SETHOSTENTINT ISC_LWRES_ENDHOSTENTINT ISC_LWRES_GETNETBYADDRINADDR ISC_LWRES_SETNETENTINT ISC_LWRES_ENDNETENTINT ISC_LWRES_GETHOSTBYADDRVOID ISC_LWRES_NEEDHERRNO ISC_LWRES_GETIPNODEPROTO ISC_LWRES_GETADDRINFOPROTO ISC_LWRES_GETNAMEINFOPROTO ISC_PLATFORM_NEEDSTRSEP ISC_PLATFORM_NEEDMEMMOVE ISC_PLATFORM_NEEDSTRTOUL LWRES_PLATFORM_NEEDSTRTOUL GENRANDOMLIB ISC_PLATFORM_NEEDSTRLCPY ISC_PLATFORM_NEEDSTRLCAT ISC_PLATFORM_NEEDSPRINTF LWRES_PLATFORM_NEEDSPRINTF ISC_PLATFORM_NEEDVSNPRINTF LWRES_PLATFORM_NEEDVSNPRINTF ISC_EXTRA_OBJS ISC_EXTRA_SRCS ISC_PLATFORM_QUADFORMAT LWRES_PLATFORM_QUADFORMAT ISC_PLATFORM_HAVESYSUNH ISC_PLATFORM_RLIMITTYPE ISC_PLATFORM_USEDECLSPEC LWRES_PLATFORM_USEDECLSPEC ISC_PLATFORM_BRACEPTHREADONCEINIT ISC_PLATFORM_HAVEIFNAMETOINDEX ISC_PLATFORM_HAVEXADD ISC_PLATFORM_HAVECMPXCHG ISC_PLATFORM_HAVEATOMICSTORE ISC_PLATFORM_USEGCCASM ISC_PLATFORM_USEOSFASM ISC_PLATFORM_USESTDASM ISC_ARCH_DIR LATEX PDFLATEX W3M XSLTPROC XMLLINT XSLT_DOCBOOK_STYLE_HTML XSLT_DOCBOOK_STYLE_XHTML XSLT_DOCBOOK_STYLE_MAN XSLT_DOCBOOK_CHUNK_HTML XSLT_DOCBOOK_CHUNK_XHTML XSLT_DOCBOOK_CHUNKTOC_HTML XSLT_DOCBOOK_CHUNKTOC_XHTML XSLT_DOCBOOK_MAKETOC_HTML XSLT_DOCBOOK_MAKETOC_XHTML XSLT_DB2LATEX_STYLE XSLT_DB2LATEX_ADMONITIONS BIND9_TOP_BUILDDIR BIND9_ISC_BUILDINCLUDE BIND9_ISCCC_BUILDINCLUDE BIND9_ISCCFG_BUILDINCLUDE BIND9_DNS_BUILDINCLUDE BIND9_LWRES_BUILDINCLUDE BIND9_BIND9_BUILDINCLUDE BIND9_VERSION LIBOBJS LTLIBOBJS' -ac_subst_files='BIND9_MAKE_INCLUDES BIND9_MAKE_RULES LIBISC_API LIBISCCC_API LIBISCCFG_API LIBDNS_API LIBBIND9_API LIBLWRES_API' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS subdirs build build_cpu build_vendor build_os host host_cpu host_vendor host_os SET_MAKE RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S STD_CINCLUDES STD_CDEFINES STD_CWARNINGS CCOPT AR ARFLAGS LN ETAGS PERL CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP ISC_SOCKADDR_LEN_T ISC_PLATFORM_HAVELONGLONG ISC_PLATFORM_HAVELIFCONF ISC_PLATFORM_NEEDSYSSELECTH LWRES_PLATFORM_NEEDSYSSELECTH USE_OPENSSL DST_OPENSSL_INC USE_GSSAPI DST_GSSAPI_INC DNS_CRYPTO_LIBS ALWAYS_DEFINES ISC_PLATFORM_USETHREADS ISC_THREAD_DIR MKDEPCC MKDEPCFLAGS MKDEPPROG IRIX_DNSSEC_WARNINGS_HACK purify_path PURIFY ECHO ac_ct_AR STRIP ac_ct_STRIP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL O A SA LIBTOOL_MKDEP_SED LIBTOOL_MODE_COMPILE LIBTOOL_MODE_INSTALL LIBTOOL_MODE_LINK LIBTOOL_ALLOW_UNDEFINED LIBTOOL_IN_MAIN LIBBIND ISC_PLATFORM_HAVEIPV6 LWRES_PLATFORM_HAVEIPV6 ISC_PLATFORM_NEEDNETINETIN6H LWRES_PLATFORM_NEEDNETINETIN6H ISC_PLATFORM_NEEDNETINET6IN6H LWRES_PLATFORM_NEEDNETINET6IN6H ISC_PLATFORM_HAVEINADDR6 LWRES_PLATFORM_HAVEINADDR6 ISC_PLATFORM_NEEDIN6ADDRANY LWRES_PLATFORM_NEEDIN6ADDRANY ISC_PLATFORM_NEEDIN6ADDRLOOPBACK LWRES_PLATFORM_NEEDIN6ADDRLOOPBACK ISC_PLATFORM_HAVEIN6PKTINFO ISC_PLATFORM_FIXIN6ISADDR ISC_IPV6_H ISC_IPV6_O ISC_ISCIPV6_O ISC_IPV6_C LWRES_HAVE_SIN6_SCOPE_ID ISC_PLATFORM_HAVESCOPEID ISC_PLATFORM_HAVEIF_LADDRREQ ISC_PLATFORM_HAVEIF_LADDRCONF ISC_PLATFORM_NEEDNTOP ISC_PLATFORM_NEEDPTON ISC_PLATFORM_NEEDATON ISC_PLATFORM_HAVESALEN LWRES_PLATFORM_HAVESALEN ISC_PLATFORM_MSGHDRFLAVOR ISC_PLATFORM_NEEDPORTT ISC_LWRES_NEEDADDRINFO ISC_LWRES_NEEDRRSETINFO ISC_LWRES_SETHOSTENTINT ISC_LWRES_ENDHOSTENTINT ISC_LWRES_GETNETBYADDRINADDR ISC_LWRES_SETNETENTINT ISC_LWRES_ENDNETENTINT ISC_LWRES_GETHOSTBYADDRVOID ISC_LWRES_NEEDHERRNO ISC_LWRES_GETIPNODEPROTO ISC_LWRES_GETADDRINFOPROTO ISC_LWRES_GETNAMEINFOPROTO ISC_PLATFORM_NEEDSTRSEP ISC_PLATFORM_NEEDMEMMOVE ISC_PLATFORM_NEEDSTRTOUL LWRES_PLATFORM_NEEDSTRTOUL GENRANDOMLIB ISC_PLATFORM_NEEDSTRLCPY ISC_PLATFORM_NEEDSTRLCAT ISC_PLATFORM_NEEDSPRINTF LWRES_PLATFORM_NEEDSPRINTF ISC_PLATFORM_NEEDVSNPRINTF LWRES_PLATFORM_NEEDVSNPRINTF ISC_EXTRA_OBJS ISC_EXTRA_SRCS ISC_PLATFORM_QUADFORMAT LWRES_PLATFORM_QUADFORMAT ISC_PLATFORM_HAVESYSUNH ISC_PLATFORM_RLIMITTYPE ISC_PLATFORM_USEDECLSPEC LWRES_PLATFORM_USEDECLSPEC ISC_PLATFORM_BRACEPTHREADONCEINIT ISC_PLATFORM_HAVEIFNAMETOINDEX ISC_PLATFORM_HAVEXADD ISC_PLATFORM_HAVECMPXCHG ISC_PLATFORM_HAVEATOMICSTORE ISC_PLATFORM_USEGCCASM ISC_PLATFORM_USEOSFASM ISC_PLATFORM_USESTDASM ISC_ARCH_DIR LATEX PDFLATEX W3M XSLTPROC XMLLINT XSLT_DOCBOOK_STYLE_HTML XSLT_DOCBOOK_STYLE_XHTML XSLT_DOCBOOK_STYLE_MAN XSLT_DOCBOOK_CHUNK_HTML XSLT_DOCBOOK_CHUNK_XHTML XSLT_DOCBOOK_CHUNKTOC_HTML XSLT_DOCBOOK_CHUNKTOC_XHTML XSLT_DOCBOOK_MAKETOC_HTML XSLT_DOCBOOK_MAKETOC_XHTML XSLT_DB2LATEX_STYLE XSLT_DB2LATEX_ADMONITIONS BIND9_TOP_BUILDDIR BIND9_ISC_BUILDINCLUDE BIND9_ISCCC_BUILDINCLUDE BIND9_ISCCFG_BUILDINCLUDE BIND9_DNS_BUILDINCLUDE BIND9_LWRES_BUILDINCLUDE BIND9_BIND9_BUILDINCLUDE BIND9_VERSION PG_CONFIG USE_DLZ DLZ_DRIVER_INCLUDES DLZ_DRIVER_LIBS DLZ_DRIVER_SRCS DLZ_DRIVER_OBJS LIBOBJS LTLIBOBJS' +ac_subst_files='BIND9_MAKE_INCLUDES BIND9_MAKE_RULES LIBISC_API LIBISCCC_API LIBISCCFG_API LIBDNS_API LIBBIND9_API LIBLWRES_API DLZ_DRIVER_RULES' # Initialize some variables set by options. ac_init_help= @@ -1082,6 +1082,20 @@ Optional Packages: --with-tags[=TAGS] include additional configurations [automatic] --with-kame=PATH use Kame IPv6 default path /usr/local/v6 + --with-dlz-postgres=PATH Build with Postgres DLZ driver yes|no|path. + (Required to use Postgres with DLZ) + --with-dlz-mysql=PATH Build with MySQL DLZ driver yes|no|path. + (Required to use MySQL with DLZ) + --with-dlz-bdb=PATH Build with Berkeley DB DLZ driver yes|no|path. + (Required to use Berkeley DB with DLZ) + --with-dlz-filesystem=PATH Build with filesystem DLZ driver yes|no. + (Required to use file system driver with DLZ) + --with-dlz-ldap=PATH Build with LDAP DLZ driver yes|no|path. + (Required to use LDAP with DLZ) + --with-dlz-odbc=PATH Build with ODBC DLZ driver yes|no|path. + (Required to use ODBC with DLZ) + --with-dlz-stub=PATH Build with stub DLZ driver yes|no. + (Required to use stub driver with DLZ) Some influential environment variables: CC C compiler command @@ -1829,6 +1843,14 @@ fi +# +# Make very sure that these are the first files processed by +# config.status, since we use the processed output as the input for +# AC_SUBST_FILE() subsitutions in other files. +# + ac_config_files="$ac_config_files make/rules make/includes" + + # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 @@ -4815,6 +4837,7 @@ presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} esac + # # was --with-openssl specified? # @@ -8134,7 +8157,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 8137 "configure"' > conftest.$ac_ext + echo '#line 8160 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -9131,7 +9154,7 @@ fi # Provide some information about the compiler. -echo "$as_me:9134:" \ +echo "$as_me:9157:" \ "checking for Fortran 77 compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 @@ -10192,11 +10215,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10195: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10218: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:10199: \$? = $ac_status" >&5 + echo "$as_me:10222: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -10435,11 +10458,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10438: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10461: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:10442: \$? = $ac_status" >&5 + echo "$as_me:10465: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -10495,11 +10518,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10498: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10521: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:10502: \$? = $ac_status" >&5 + echo "$as_me:10525: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -12680,7 +12703,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:15001: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:14982: \$? = $ac_status" >&5 + echo "$as_me:15005: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -15035,11 +15058,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15038: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15061: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:15042: \$? = $ac_status" >&5 + echo "$as_me:15065: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16396,7 +16419,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:17357: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:17338: \$? = $ac_status" >&5 + echo "$as_me:17361: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -17391,11 +17414,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:17394: $lt_compile\"" >&5) + (eval echo "\"\$as_me:17417: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:17398: \$? = $ac_status" >&5 + echo "$as_me:17421: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -19430,11 +19453,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:19433: $lt_compile\"" >&5) + (eval echo "\"\$as_me:19456: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:19437: \$? = $ac_status" >&5 + echo "$as_me:19460: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -19673,11 +19696,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:19676: $lt_compile\"" >&5) + (eval echo "\"\$as_me:19699: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:19680: \$? = $ac_status" >&5 + echo "$as_me:19703: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -19733,11 +19756,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:19736: $lt_compile\"" >&5) + (eval echo "\"\$as_me:19759: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:19740: \$? = $ac_status" >&5 + echo "$as_me:19763: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -21918,7 +21941,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5 +echo $ECHO_N "checking for Postgres DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_postgres or --without-dlz_postgres was given. +if test "${with_dlz_postgres+set}" = set; then + withval="$with_dlz_postgres" + use_dlz_postgres="$withval" +else + use_dlz_postgres="no" +fi; + +if test "$use_dlz_postgres" = "yes" +then + # User did not specify a path - guess it + # Ask Postgres to tell us where it is + + for ac_prog in pg_config +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PG_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PG_CONFIG="$PG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +PG_CONFIG=$ac_cv_path_PG_CONFIG + +if test -n "$PG_CONFIG"; then + echo "$as_me:$LINENO: result: $PG_CONFIG" >&5 +echo "${ECHO_T}$PG_CONFIG" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$PG_CONFIG" && break +done +test -n "$PG_CONFIG" || PG_CONFIG="not found" + + + if test "$PG_CONFIG" != "not found" + then + use_dlz_postgres=`$PG_CONFIG --includedir` + use_dlz_postgres_lib=`$PG_CONFIG --libdir` + fi +fi + +if test "$use_dlz_postgres" = "yes" +then + # User did not specify path and Postgres didn't say - guess it + + pgdirs="/usr /usr/local /usr/local/pgsql /usr/pkg" + for d in $pgdirs + do + if test -f $d/include/libpq-fe.h + then + use_dlz_postgres=$d/include + use_dlz_postgres_lib=$d/lib + break + fi + done +fi + +if test "$use_dlz_postgres" = "yes" +then + # Still no joy, give up + + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: No pg_config and PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path or put pg_config in your path" >&5 +echo "$as_me: error: No pg_config and PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path or put pg_config in your path" >&2;} + { (exit 1); exit 1; }; } +fi + +case "$use_dlz_postgres" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + + USE_DLZ="$USE_DLZ -DDLZ_POSTGRES" + for i in dlz_postgres_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "-I$use_dlz_postgres" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_postgres" + fi + if test -n "-L$use_dlz_postgres_lib -lpq" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_postgres_lib -lpq" + fi + + + echo "$as_me:$LINENO: result: using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres" >&5 +echo "${ECHO_T}using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres" >&6 + ;; +esac + + +# +# Was --with-dlz-mysql specified? +# + +echo "$as_me:$LINENO: checking for MySQL DLZ driver" >&5 +echo $ECHO_N "checking for MySQL DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_mysql or --without-dlz_mysql was given. +if test "${with_dlz_mysql+set}" = set; then + withval="$with_dlz_mysql" + use_dlz_mysql="$withval" +else + use_dlz_mysql="no" +fi; + +if test "$use_dlz_mysql" = "yes" +then + # User did not specify a path - guess it + mysqldirs="/usr /usr/local /usr/local/mysql /usr/pkg" + for d in $mysqldirs + do + if test -f $d/include/mysql/mysql.h + then + use_dlz_mysql=$d + break + fi + done +fi + +if test "$use_dlz_mysql" = "yes" +then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path" >&5 +echo "$as_me: error: MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path" >&2;} + { (exit 1); exit 1; }; } +fi + +case "$use_dlz_mysql" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + + USE_DLZ="$USE_DLZ -DDLZ_MYSQL" + for i in dlz_mysql_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "-I$use_dlz_mysql/include/mysql" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_mysql/include/mysql" + fi + if test -n "-L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lm" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lm" + fi + + + echo "$as_me:$LINENO: result: using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql" >&5 +echo "${ECHO_T}using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql" >&6 + ;; +esac + + +# +# Was --with-dlz-bdb specified? +# + +echo "$as_me:$LINENO: checking for Berkeley DB DLZ driver" >&5 +echo $ECHO_N "checking for Berkeley DB DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_bdb or --without-dlz_bdb was given. +if test "${with_dlz_bdb+set}" = set; then + withval="$with_dlz_bdb" + use_dlz_bdb="$withval" +else + use_dlz_bdb="no" +fi; + +case "$use_dlz_bdb" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + if test "$use_dlz_bdb" = "yes" + then + # User did not specify a path - guess directories + bdbdirs="/usr/local /usr/pkg /usr" + elif test -d "$use_dlz_bdb" + then + # User specified directory and it exists + bdbdirs="$use_dlz_bdb" + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: path $use_dlz_bdb does not exist" >&5 +echo "$as_me: error: path $use_dlz_bdb does not exist" >&2;} + { (exit 1); exit 1; }; } + bdbdirs="" + fi + + # Use path we were given or guessed. This is insanely + # complicated because we have to search for a bunch of + # platform-specific variations and have to check + # separately for include and library directories. + + # Set both to yes, so we can check them later + dlz_bdb_inc="yes" + dlz_bdb_libs="yes" + + for dd in $bdbdirs + do + # Skip nonexistant directories + if test ! -d "$dd" + then + continue + fi + + # Check other locations for includes. + # Order is important (sigh). + + bdb_incdirs="/ /db42/ /db41/ /db4/ /db/" + for d in $bdb_incdirs + do + if test -f "$dd/include${d}db.h" + then + dlz_bdb_inc="-I$dd/include${d}" + break + fi + done + + # Give up on this directory if we couldn't + # find the include subdir + + if test "$dlz_bdb_inc" = "yes" + then + continue + fi + + # Look for libname other than libdb.so. + # Order is important (sigh). + + bdb_libnames="db42 db-4.2 db41 db-4.1 db" + for d in $bdb_libnames + do + if test -f "$dd/lib/lib${d}.so" + then + if test "$dd" != "/usr" + then + dlz_bdb_libs="-L${dd}/lib " + fi + dlz_bdb_libs="${dlz_bdb_libs}-l${d}" + break + fi + done + + # If we found both incdir and lib, we're done + if test "$dlz_bdb_libs" != "yes" + then + break + fi + + # Otherwise, we're starting over + + dlz_bdb_inc="yes" + dlz_bdb_libs="yes" + done + + # Done searching, now make sure we got everything. + + if test "$dlz_bdb_inc" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: could not find Berkeley DB include directory" >&5 +echo "$as_me: error: could not find Berkeley DB include directory" >&2;} + { (exit 1); exit 1; }; } + fi + + if test "$dlz_bdb_libs" = "yes" + then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: could not find Berkeley DB library" >&5 +echo "$as_me: error: could not find Berkeley DB library" >&2;} + { (exit 1); exit 1; }; } + fi + + + USE_DLZ="$USE_DLZ -DDLZ_BDB" + for i in dlz_bdb_driver dlz_bdbhpt_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "$dlz_bdb_inc" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES $dlz_bdb_inc" + fi + if test -n "$dlz_bdb_libs" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS $dlz_bdb_libs" + fi + + + echo "$as_me:$LINENO: result: using Berkeley DB: $dlz_bdb_inc $dlz_bdb_libs" >&5 +echo "${ECHO_T}using Berkeley DB: $dlz_bdb_inc $dlz_bdb_libs" >&6 + + ac_config_files="$ac_config_files contrib/dlz/bin/dlzbdb/Makefile" + + ;; +esac + + +# +# Was --with-dlz-filesystem specified? +# + +echo "$as_me:$LINENO: checking for file system DLZ driver" >&5 +echo $ECHO_N "checking for file system DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_filesystem or --without-dlz_filesystem was given. +if test "${with_dlz_filesystem+set}" = set; then + withval="$with_dlz_filesystem" + use_dlz_filesystem="$withval" +else + use_dlz_filesystem="no" +fi; + +case "$use_dlz_filesystem" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + + USE_DLZ="$USE_DLZ -DDLZ_FILESYSTEM" + for i in dlz_filesystem_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES " + fi + if test -n "" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS " + fi + + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; +esac + + +# +# Was --with-dlz-ldap specified? +# + +echo "$as_me:$LINENO: checking for LDAP DLZ driver" >&5 +echo $ECHO_N "checking for LDAP DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_ldap or --without-dlz_ldap was given. +if test "${with_dlz_ldap+set}" = set; then + withval="$with_dlz_ldap" + use_dlz_ldap="$withval" +else + use_dlz_ldap="no" +fi; + +if test "$use_dlz_ldap" = "yes" +then + # User did not specify a path - guess it + ldapdirs="/usr /usr/local /usr/pkg" + for d in $ldapdirs + do + if test -f $d/include/ldap.h + then + use_dlz_ldap=$d + break + fi + done +fi + +if test "$use_dlz_ldap" = "yes" +then + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path" >&5 +echo "$as_me: error: LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path" >&2;} + { (exit 1); exit 1; }; } +fi + +case "$use_dlz_ldap" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + + USE_DLZ="$USE_DLZ -DDLZ_LDAP" + for i in dlz_ldap_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "-I$use_dlz_ldap/include" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_ldap/include" + fi + if test -n "-L$use_dlz_ldap/lib -lldap -llber" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_ldap/lib -lldap -llber" + fi + + + echo "$as_me:$LINENO: result: using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include" >&5 +echo "${ECHO_T}using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include" >&6 + ;; +esac + + +# +# Was --with-dlz-odbc specified? +# + +echo "$as_me:$LINENO: checking for ODBC DLZ driver" >&5 +echo $ECHO_N "checking for ODBC DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_odbc or --without-dlz_odbc was given. +if test "${with_dlz_odbc+set}" = set; then + withval="$with_dlz_odbc" + use_dlz_odbc="$withval" +else + use_dlz_odbc="no" +fi; + +if test "$use_dlz_odbc" = "yes" +then + # User did not specify a path - guess it + odbcdirs="/usr /usr/local /usr/pkg" + for d in $odbcdirs + do + if test -f $d/include/sql.h -a -f $d/lib/libodbc.a + then + use_dlz_odbc=$d + break + fi + done +fi + +case "$use_dlz_odbc" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + yes) + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + { { echo "$as_me:$LINENO: error: ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path" >&5 +echo "$as_me: error: ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path" >&2;} + { (exit 1); exit 1; }; } + ;; + *) + + USE_DLZ="$USE_DLZ -DDLZ_ODBC" + for i in dlz_odbc_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "-I$use_dlz_odbc/include" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_odbc/include" + fi + if test -n "-L$use_dlz_odbc/lib -lodbc" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_odbc/lib -lodbc" + fi + + + echo "$as_me:$LINENO: result: using ODBC from $use_dlz_odbc" >&5 +echo "${ECHO_T}using ODBC from $use_dlz_odbc" >&6 + ;; +esac + + +# +# Was --with-dlz-stub specified? +# + +echo "$as_me:$LINENO: checking for stub DLZ driver" >&5 +echo $ECHO_N "checking for stub DLZ driver... $ECHO_C" >&6 + +# Check whether --with-dlz_stub or --without-dlz_stub was given. +if test "${with_dlz_stub+set}" = set; then + withval="$with_dlz_stub" + use_dlz_stub="$withval" +else + use_dlz_stub="no" +fi; + +case "$use_dlz_stub" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) + + + USE_DLZ="$USE_DLZ -DDLZ_STUB" + for i in dlz_stub_driver + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES " + fi + if test -n "" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS " + fi + + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; +esac + + +# Add any additional DLZ drivers here. + +# +# Finally, some generic stuff that applies to all drivers, assuming +# we're compiling DLZ at all. +# +if test -n "$USE_DLZ" +then + # + # Where to find DLZ driver header files. + # + DLZ_DRIVER_INCLUDES="-I$dlzdir/include $DLZ_DRIVER_INCLUDES" + + # + # Initialization and shutdown wrappers, helper functions. + # + DLZ_DRIVER_SRCS="$dlzdir/dlz_drivers.c $dlzdir/sdlz_helper.c $DLZ_DRIVER_SRCS" + DLZ_DRIVER_OBJS="dlz_drivers.$O sdlz_helper.$O $DLZ_DRIVER_OBJS" +fi + + +echo "$as_me:$LINENO: checking for DLZ" >&5 +echo $ECHO_N "checking for DLZ... $ECHO_C" >&6 + +if test -n "$USE_DLZ" +then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + USE_DLZ="-DDLZ $USE_DLZ" + DLZ_DRIVER_RULES=contrib/dlz/drivers/rules + ac_config_files="$ac_config_files $DLZ_DRIVER_RULES" + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + DLZ_DRIVER_RULES=/dev/null +fi + + + + + + + + + # # Commands to run at the end of config.status. # Don't just put these into configure, it won't work right if somebody @@ -28399,11 +29090,19 @@ LIBLWRES_API=$srcdir/lib/lwres/api ac_config_commands="$ac_config_commands chmod" +# +# Files to configure. These are listed here because we used to +# specify them as arguments to AC_OUTPUT. It's (now) ok to move these +# elsewhere if there's a good reason for doing so. +# + + ac_config_files="$ac_config_files Makefile make/Makefile make/mkdep lib/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/nls/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile bin/rndc/Makefile bin/rndc/unix/Makefile bin/dig/Makefile bin/nsupdate/Makefile bin/tests/Makefile bin/tests/names/Makefile bin/tests/master/Makefile bin/tests/rbt/Makefile bin/tests/db/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/dst/Makefile bin/tests/mem/Makefile bin/tests/net/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/lwresd/Makefile bin/tests/system/tkey/Makefile bin/tests/headerdep_test.sh bin/dnssec/Makefile doc/Makefile doc/arm/Makefile doc/misc/Makefile isc-config.sh doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl" + + # # Do it # - ac_config_files="$ac_config_files make/rules make/includes Makefile make/Makefile make/mkdep lib/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/nls/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile bin/rndc/Makefile bin/rndc/unix/Makefile bin/dig/Makefile bin/nsupdate/Makefile bin/tests/Makefile bin/tests/names/Makefile bin/tests/master/Makefile bin/tests/rbt/Makefile bin/tests/db/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/dst/Makefile bin/tests/mem/Makefile bin/tests/net/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/lwresd/Makefile bin/tests/system/tkey/Makefile bin/tests/headerdep_test.sh bin/dnssec/Makefile doc/Makefile doc/arm/Makefile doc/misc/Makefile isc-config.sh doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -28934,6 +29633,8 @@ do # Handling of arguments. "make/rules" ) CONFIG_FILES="$CONFIG_FILES make/rules" ;; "make/includes" ) CONFIG_FILES="$CONFIG_FILES make/includes" ;; + "contrib/dlz/bin/dlzbdb/Makefile" ) CONFIG_FILES="$CONFIG_FILES contrib/dlz/bin/dlzbdb/Makefile" ;; + "$DLZ_DRIVER_RULES" ) CONFIG_FILES="$CONFIG_FILES $DLZ_DRIVER_RULES" ;; "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "make/Makefile" ) CONFIG_FILES="$CONFIG_FILES make/Makefile" ;; "make/mkdep" ) CONFIG_FILES="$CONFIG_FILES make/mkdep" ;; @@ -29263,6 +29964,12 @@ s,@BIND9_DNS_BUILDINCLUDE@,$BIND9_DNS_BUILDINCLUDE,;t t s,@BIND9_LWRES_BUILDINCLUDE@,$BIND9_LWRES_BUILDINCLUDE,;t t s,@BIND9_BIND9_BUILDINCLUDE@,$BIND9_BIND9_BUILDINCLUDE,;t t s,@BIND9_VERSION@,$BIND9_VERSION,;t t +s,@PG_CONFIG@,$PG_CONFIG,;t t +s,@USE_DLZ@,$USE_DLZ,;t t +s,@DLZ_DRIVER_INCLUDES@,$DLZ_DRIVER_INCLUDES,;t t +s,@DLZ_DRIVER_LIBS@,$DLZ_DRIVER_LIBS,;t t +s,@DLZ_DRIVER_SRCS@,$DLZ_DRIVER_SRCS,;t t +s,@DLZ_DRIVER_OBJS@,$DLZ_DRIVER_OBJS,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t /@BIND9_MAKE_INCLUDES@/r $BIND9_MAKE_INCLUDES @@ -29281,6 +29988,8 @@ s,@LIBDNS_API@,,;t t s,@LIBBIND9_API@,,;t t /@LIBLWRES_API@/r $LIBLWRES_API s,@LIBLWRES_API@,,;t t +/@DLZ_DRIVER_RULES@/r $DLZ_DRIVER_RULES +s,@DLZ_DRIVER_RULES@,,;t t CEOF _ACEOF diff --git a/configure.in b/configure.in index e8da02eef62..e1f883a68b5 100644 --- a/configure.in +++ b/configure.in @@ -18,10 +18,10 @@ AC_DIVERT_PUSH(1)dnl esyscmd([sed "s/^/# /" COPYRIGHT])dnl AC_DIVERT_POP()dnl -AC_REVISION($Revision: 1.391 $) +AC_REVISION($Revision: 1.392 $) AC_INIT(lib/dns/name.c) -AC_PREREQ(2.13) +AC_PREREQ(2.59) AC_CONFIG_HEADER(config.h) AC_CONFIG_SUBDIRS(lib/bind) @@ -38,6 +38,13 @@ AC_SUBST(STD_CDEFINES) AC_SUBST(STD_CWARNINGS) AC_SUBST(CCOPT) +# +# Make very sure that these are the first files processed by +# config.status, since we use the processed output as the input for +# AC_SUBST_FILE() subsitutions in other files. +# +AC_CONFIG_FILES([make/rules make/includes]) + AC_PATH_PROG(AR, ar) ARFLAGS="cruv" AC_SUBST(AR) @@ -355,6 +362,7 @@ AC_SUBST(LWRES_PLATFORM_NEEDSYSSELECTH) # AC_C_BIGENDIAN + # # was --with-openssl specified? # @@ -979,6 +987,7 @@ case "$enable_libbind" in ;; esac + # # Here begins a very long section to determine the system's networking # capabilities. The order of the tests is signficant. @@ -2180,6 +2189,46 @@ LIBBIND9_API=$srcdir/lib/bind9/api AC_SUBST_FILE(LIBLWRES_API) LIBLWRES_API=$srcdir/lib/lwres/api +# +# Configure any DLZ drivers. +# +# If config.dlz.in selects one or more DLZ drivers, it will set +# USE_DLZ to a non-empty value, which will be our clue to +# enable the DLZ core functions. +# +# This section has to come after the libtool stuff because it needs to +# know how to name the driver object files. +# + +USE_DLZ="" +DLZ_DRIVER_INCLUDES="" +DLZ_DRIVER_LIBS="" +DLZ_DRIVER_SRCS="" +DLZ_DRIVER_OBJS="" + +sinclude(contrib/dlz/config.dlz.in) + +AC_MSG_CHECKING(for DLZ) + +if test -n "$USE_DLZ" +then + AC_MSG_RESULT(yes) + USE_DLZ="-DDLZ $USE_DLZ" + DLZ_DRIVER_RULES=contrib/dlz/drivers/rules + AC_CONFIG_FILES([$DLZ_DRIVER_RULES]) +else + AC_MSG_RESULT(no) + DLZ_DRIVER_RULES=/dev/null +fi + +AC_SUBST(USE_DLZ) +AC_SUBST(DLZ_DRIVER_INCLUDES) +AC_SUBST(DLZ_DRIVER_LIBS) +AC_SUBST(DLZ_DRIVER_SRCS) +AC_SUBST(DLZ_DRIVER_OBJS) +AC_SUBST_FILE(DLZ_DRIVER_RULES) + + # # Commands to run at the end of config.status. # Don't just put these into configure, it won't work right if somebody @@ -2191,12 +2240,12 @@ AC_CONFIG_COMMANDS( [chmod a+x isc-config.sh]) # -# Do it +# Files to configure. These are listed here because we used to +# specify them as arguments to AC_OUTPUT. It's (now) ok to move these +# elsewhere if there's a good reason for doing so. # -AC_OUTPUT( - make/rules - make/includes +AC_CONFIG_FILES([ Makefile make/Makefile make/mkdep @@ -2271,7 +2320,13 @@ AC_OUTPUT( doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl -) +]) + +# +# Do it +# + +AC_OUTPUT # Tell Emacs to edit this file in shell mode. # Local Variables: diff --git a/contrib/dlz/bin/dlzbdb/.cvsignore b/contrib/dlz/bin/dlzbdb/.cvsignore new file mode 100644 index 00000000000..5e57bac6284 --- /dev/null +++ b/contrib/dlz/bin/dlzbdb/.cvsignore @@ -0,0 +1,3 @@ +Makefile +dlzbdb +*.o diff --git a/contrib/dlz/bin/dlzbdb/Makefile.in b/contrib/dlz/bin/dlzbdb/Makefile.in new file mode 100644 index 00000000000..140ce626a17 --- /dev/null +++ b/contrib/dlz/bin/dlzbdb/Makefile.in @@ -0,0 +1,73 @@ +# Copyright (C) 1998-2001 Internet Software Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# $Id: Makefile.in,v 1.2 2005/09/05 00:10:54 marka Exp $ + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +DLZINCLUDES = @DLZ_BDB_INC@ + +CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include \ + ${ISC_INCLUDES} ${DLZINCLUDES} + +CDEFINES = @USE_DLZ_BDB@ +CWARNINGS = + +DLZLIBS = @DLZ_BDB_LIBS@ +ISCLIBS = ../../lib/isc/libisc.@A@ + +DEPLIBS = ${ISCDEPLIBS} + +LIBS = ${ISCLIBS} ${DLZLIBS} @LIBS@ + +TARGETS = dlzbdb + +SRCS = dlzbdb.c + +#MANPAGES = + +#HTMLPAGES = + +#MANOBJS = ${MANPAGES} ${HTMLPAGES} + +@BIND9_MAKE_RULES@ + +dlzbdb.@O@: dlzbdb.c + ${LIBTOOL} ${CC} ${ALL_CFLAGS} -DVERSION=\"${VERSION}\" \ + -c ${srcdir}/dlzbdb.c + +dlzbdb: dlzbdb.@O@ ${DEPLIBS} + ${LIBTOOL} ${PURIFY} ${CC} ${CFLAGS} -o $@ dlzbdb.@O@ ${LIBS} + +doc man:: ${MANOBJS} + +#docclean manclean maintainer-clean:: +# rm -f ${MANOBJS} + +clean distclean maintainer-clean:: + rm -f ${TARGETS} + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir} +# $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8 + +install:: dlzbdb installdirs + ${LIBTOOL} ${INSTALL_PROGRAM} dlzbdb ${DESTDIR}${sbindir} diff --git a/contrib/dlz/bin/dlzbdb/dlzbdb.c b/contrib/dlz/bin/dlzbdb/dlzbdb.c new file mode 100644 index 00000000000..aedc9531717 --- /dev/null +++ b/contrib/dlz/bin/dlzbdb/dlzbdb.c @@ -0,0 +1,1273 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_BDB + +/* + * exit codes + * 0 everything ok + * 1 error parsing command line + * 2 Missing, too many or invalid combination of command line parameters + * 3 Unable to open BDB database. + * 4 Unable to allocate memory for, or create lexer. + * 5 unable to perform BDB cursor operation + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* shut up compiler warnings about no previous prototype */ + +static void +show_usage(void); + +int +getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey); + +int +gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey); + +void +bdb_cleanup(void); + +isc_result_t +bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags); + +void +put_data(isc_boolean_t dns_data, char *input_key, char *input_data); + +void +insert_data(void); + +isc_result_t +openBDB(void); + +isc_result_t +open_lexer(void); + +void +close_lexer(void); + +isc_result_t +bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata); + +void +operation_add(void); + +void +operation_bulk(void); + +void +operation_listOrDelete(isc_boolean_t dlt); + + +/*% + * Maximum length of a single data line that + * may be inserted into database by this program. + * If you need to insert a line of data that is more + * than 10,000 characters change this definition. + */ + +#define max_data_len 10000 + +/*% + * BDB database names. If you want to use different + * database names change them here. + */ + +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_host "dns_host" +#define dlz_client "dns_client" + + +/*% + * Error code returned by BDB secondary index callback functions. + * This error is returned if the callback function could not create + * the secondary index for any reason. + */ + +#define BDBparseErr 1 + +/* A struct to hold all the relevant info about the database */ + +typedef struct bdb_instance { + DB_ENV *dbenv; /* BDB environment */ + DB *data; /* dns_data database handle */ + DBC *cursor; /* database cursor */ + DBC *cursor2; /* second cursor used during list operation. */ + DBC *cursor3; /* third cursor used during list operation */ + DBC *cursor4; /* fourth cursor used during list operation */ + DB *zone; /* zone database handle */ + DB *host; /* host database handle */ + DB *client; /* client database handle */ +} bdb_instance_t; + +/* Possible operations */ + +#define list 1 /* list data */ +#define dele 2 /* delete data */ +#define add 3 /* add a single piece of data */ +#define bulk 4 /* bulk load data */ + + +/*% + * quit macro is used instead of exit. quit always trys to close the lexer + * and the BDB database before exiting. + */ + +#define quit(i) close_lexer(); bdb_cleanup(); exit(i); + +/*% + * checkOp is used to verify that only one operation (list, del, add, + * bulk from file, bulk from stdin) is specified on the command line. + * This prevents a user from specifying two operations on the command + * line, which would make no sense anyway. + */ + +#define checkOp(x) if (x != 0) {fprintf(stderr, "\nonly one operation "\ + "(l e d a f s) may be specified\n"); quit(2);} + +/*% + * checkParam is used to only allow a parameter to be specified once. + * I.E. the parameter key can only be used on the command line once. + * any attempt to use it twice causes an error. + */ + +#define checkParam(x, y) if (x != NULL) {fprintf(stderr, "\n%s may only "\ + "be specified once\n", y); quit(2);} + +/*% + * checkInvalidParam is used to only allow paramters which make sense for + * the operation selected. I.E. passing the key parameter makes no sense + * for the add operation, and thus it isn't allowed. + */ + +#define checkInvalidParam(x, y, z) if (x != NULL) {fprintf(stderr, "\n%s "\ + "may not be specified %s\n", y, z); quit(2);} + +/*% + * checkInvalidOption is used to only allow paramters which make sense for + * the operation selected - but checks boolean options. + * I.E. passing the "b" bare_list parameter makes no sense for the add + * operation, and thus it isn't allowed. + * if w == x then output error message "flag", "message" + */ + +#define checkInvalidOption(w, x, y, z) if (w == x) {fprintf(stderr, "\n%s "\ + "may not be specified %s\n", y, z); quit(2);} + +/* Global Variables */ + +int operation = 0; /*%< operation to perform. */ +/*% allow new lock files or DB to be created. */ +isc_boolean_t create_allowed = isc_boolean_false; +char *key = NULL; /*%< key to use in list & del operations */ + +/*% dump DB in DLZBDB bulk format */ +isc_boolean_t list_everything = isc_boolean_false; +unsigned int key_val; /*%< key as unsigned int used in list & del operations */ +char *zone = NULL; /*%< zone to use in list operations */ +char *host = NULL; /*%< host to use in list operations */ +char *c_zone = NULL; /*%< client zone to use in list operations */ +char *c_ip = NULL; /*%< client IP to use in list operations */ +char *a_data = NULL; /*%< data in add operation */ +char *bulk_file = NULL; /*%< bulk data file to load */ +char *db_envdir = NULL; /*%< BDB environment location */ +char *db_file = NULL; /*%< BDB database file location. */ +bdb_instance_t db; /* BDB instance we are operating on */ +isc_lex_t *lexer = NULL; /*%< lexer for use to use in parsing input */ +isc_mem_t *lex_mctx = NULL; /*%< memory context for lexer */ +char lex_data_buf[max_data_len]; /*%< data array to use for lex_buffer below */ +isc_buffer_t lex_buffer; /*%< buffer for lexer during add operation */ + + +/*% + * Displays usage message + */ + +static void +show_usage(void) { + fprintf(stderr, "\n\n\ +---Usage:---------------------------------------------------------------------\ +\n\n\ + List data:\n\ + dlzbdb -l [-k key] [-z zone] [-h host] [-c client_zone] [-i client_ip]\n\ + BDB_environment BDB_database\n\n\ + Delete data:\n\ + dlzbdb -d [-k key] [-c client_zone] [-i client_ip]\n\ + BDB_environment BDB_database\n\n\ + Bulk load data from file:\n\ + dlzbdb -f file_to_load BDB_environment BDB_database\n\n\ + Bulk load data from stdin\n\ + dlzbdb -s BDB_environment BDB_database\n\n\ + Add data:\n\ + dlzbdb -a \"dns data to be added\" BDB_environment BDB_database\n\n\ + Export data:\n\ + dlzbdb -e BDB_environment BDB_database\n\n\ + Normally operations can only be performed on an existing database files.\n\ + Use the -n flag with any operation to allow files to be created.\n\ + Existing files will NOT be truncated by using the -n flag.\n\ + The -n flag will allow a new database to be created, or allow new\n\ + environment files to be created for an existing database.\n\n\ +---Format for -f & -a options:------------------------------------------------\ +\n\n\ +db_type zone host dns_type ttl ip\n\ +db_type zone host dns_type ttl mx_priority mail_host\n\ +db_type zone host dns_type ttl nm_svr resp_psn serial refresh retry expire min\ +\n\ +db_type zone client_ip\n\n\ +---Examples:------------------------------------------------------------------\ +\n\n\ +d mynm.com www A 10 127.0.0.1\n\ +d mynm.com @ MX 10 5 mail\n\ +d mynm.com @ SOA 10 ns1.mynm.com. root.mynm.com. 2 28800 7200 604800 86400\n\ +c mynm.com 127.0.0.1\n\ +c mynm.com 192.168.0.10\n\ +"); +quit(1); +} + + +/*% BDB callback to create zone secondary index */ + +int +getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) { + char *tmp; + char *left; + char *right; + int result=0; + + UNUSED(dbp); + UNUSED(pkey); + + /* Allocate memory to use in parsing the string */ + tmp = right = malloc(pdata->size + 1); + + /* verify memory was allocated */ + if (right == NULL) { + result = BDBparseErr; + goto getzone_cleanup; + } + + /* copy data string into newly allocated memory */ + strncpy(right, pdata->data, pdata->size); + right[pdata->size] = '\0'; + + /* split string at the first space */ + left = isc_string_separate(&right, " "); + + /* copy string for "zone" secondary index */ + skey->data = strdup(left); + if (skey->data == NULL) { + result = BDBparseErr; + goto getzone_cleanup; + } + /* set required values for BDB */ + skey->size = strlen(skey->data); + skey->flags = DB_DBT_APPMALLOC; + + getzone_cleanup: + + /* cleanup memory */ + if (tmp != NULL) + free(tmp); + + return result; +} + +/*% + * BDB callback to create host secondary index + */ + +int +gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) { + char *tmp; + char *left; + char *right; + int result=0; + + UNUSED(dbp); + UNUSED(pkey); + + /* allocate memory to use in parsing the string */ + tmp = right = malloc(pdata->size + 1); + + /* verify memory was allocated */ + if (tmp == NULL) { + result = BDBparseErr; + goto gethost_cleanup; + } + + /* copy data string into newly allocated memory */ + strncpy(right, pdata->data, pdata->size); + right[pdata->size] = '\0'; + + /* we don't care about left string. */ + /* memory of left string will be freed when tmp is freed. */ + isc_string_separate(&right, " "); + + /* verify right still has some characters left */ + if (right == NULL) { + result = BDBparseErr; + goto gethost_cleanup; + } + + /* get "host" from data string */ + left = isc_string_separate(&right, " "); + /* copy string for "host" secondary index */ + skey->data = strdup(left); + if (skey->data == NULL) { + result = BDBparseErr; + goto gethost_cleanup; + } + /* set required values for BDB */ + skey->size = strlen(skey->data); + skey->flags = DB_DBT_APPMALLOC; + + gethost_cleanup: + + /* cleanup memory */ + if (tmp != NULL) + free(tmp); + + return result; +} + +/*% + * Performs BDB cleanup. Close each database that we opened. + * Close environment. Set each var to NULL so we know they + * were closed and don't accidentally try to close them twice. + */ + +void +bdb_cleanup(void) { + + /* close cursors */ + if (db.cursor4 != NULL) { + db.cursor4->c_close(db.cursor4); + db.cursor4 = NULL; + } + + if (db.cursor3 != NULL) { + db.cursor3->c_close(db.cursor3); + db.cursor3 = NULL; + } + + if (db.cursor2 != NULL) { + db.cursor2->c_close(db.cursor2); + db.cursor2 = NULL; + } + + if (db.cursor != NULL) { + db.cursor->c_close(db.cursor); + db.cursor = NULL; + } + + /* close databases */ + if (db.data != NULL) { + db.data->close(db.data, 0); + db.data = NULL; + } + if (db.host != NULL) { + db.host->close(db.host, 0); + db.host = NULL; + } + if (db.zone != NULL) { + db.zone->close(db.zone, 0); + db.zone = NULL; + } + if (db.client != NULL) { + db.client->close(db.client, 0); + db.client = NULL; + } + + /* close environment */ + if (db.dbenv != NULL) { + db.dbenv->close(db.dbenv, 0); + db.dbenv = NULL; + } +} + +/*% Initializes, sets flags and then opens Berkeley databases. */ + +isc_result_t +bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags) { + + int result; + int createFlag = 0; + + /* Initialize the database. */ + if ((result = db_create(db_out, db.dbenv, 0)) != 0) { + fprintf(stderr, "BDB could not initialize %s database. BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* set database flags. */ + if ((result = (*db_out)->set_flags(*db_out, flags)) != 0) { + fprintf(stderr, "BDB could not set flags for %s database. BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + if (create_allowed == isc_boolean_true) { + createFlag = DB_CREATE; + } + /* open the database. */ + if ((result = (*db_out)->open(*db_out, NULL, db_file, db_name, db_type, + createFlag, 0)) != 0) { + fprintf(stderr, "BDB could not open %s database in %s. BDB error: %s", + db_name, db_file, db_strerror(result)); + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +/*% + * parses input and adds it to the BDB database + * Lexer should be instantiated, and either a file or buffer opened for it. + * The insert_data function is used by both the add, and bulk insert + * operations + */ + +void +put_data(isc_boolean_t dns_data, char *input_key, char *input_data) { + + int bdbres; + DBT key, data; + + /* make sure key & data are completely empty */ + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + /* if client data, setup key for insertion */ + if (!dns_data && input_key != NULL) { + key.data = input_key; + key.size = strlen(input_key); + key.flags = 0; + } + /* always setup data for insertion */ + data.data = input_data; + data.size = strlen(input_data); + data.flags = 0; + + /* execute insert against appropriate database. */ + if (dns_data) { + bdbres = db.data->put(db.data, NULL, &key, &data, DB_APPEND); + } else { + bdbres = db.client->put(db.client, NULL, &key, &data, 0); + } + + /* if something went wrong, log error and quit */ + if (bdbres != 0) { + fprintf(stderr, "BDB could not insert data. Error: %s", + db_strerror(bdbres)); + quit(5); + } +} + +void +insert_data(void) { + unsigned int opt = + ISC_LEXOPT_EOL | /* Want end-of-line token. */ + ISC_LEXOPT_EOF | /* Want end-of-file token. */ + ISC_LEXOPT_QSTRING | /* Recognize qstrings. */ + ISC_LEXOPT_QSTRINGMULTILINE; /* Allow multiline "" strings */ + + isc_result_t result; + isc_token_t token; /* token from lexer */ + isc_boolean_t loop = isc_boolean_true; + isc_boolean_t have_czone = isc_boolean_false; + char data_arr[max_data_len]; + isc_buffer_t buf; + char data_arr2[max_data_len]; + isc_buffer_t buf2; + char data_type = 'u'; /* u =unknown, b =bad token, d/D =DNS, c/C =client IP */ + + /* Initialize buffers */ + isc_buffer_init(&buf, &data_arr, max_data_len); + isc_buffer_init(&buf2, &data_arr2, max_data_len); + + while (loop) { + result = isc_lex_gettoken(lexer, opt, &token); + if (result != ISC_R_SUCCESS) + goto data_cleanup; + + switch(token.type) { + case isc_tokentype_string: + if (data_type == 'u') { + /* store data_type */ + strncpy(&data_type, token.value.as_pointer, 1); + /* verify data_type was specified correctly on input */ + if (strlen(token.value.as_pointer) > 1 || ( + data_type != 'd' && data_type != 'D' && + data_type != 'c' && data_type != 'C') ) { + /* if not, set to 'b' so this line is ignored. */ + data_type = 'b'; + } + } else if (data_type == 'c' || data_type == 'C') { + if (have_czone == isc_boolean_true) { + isc_buffer_putstr(&buf2, token.value.as_pointer); + /* add string terminator to buffer */ + isc_buffer_putmem(&buf2, "\0", 1); + } else { + isc_buffer_putstr(&buf, token.value.as_pointer); + /* add string terminator to buffer */ + isc_buffer_putmem(&buf, "\0", 1); + have_czone = isc_boolean_true; + } + } else { + isc_buffer_putstr(&buf, token.value.as_pointer); + isc_buffer_putstr(&buf, " "); + } + break; + case isc_tokentype_qstring: + isc_buffer_putstr(&buf, "\""); + isc_buffer_putstr(&buf, token.value.as_pointer); + isc_buffer_putstr(&buf, "\" "); + break; + case isc_tokentype_eol: + case isc_tokentype_eof: + + if ((data_type != 'u' && isc_buffer_usedlength(&buf) > 0) || data_type == 'b') { + /* perform insert operation */ + if (data_type == 'd' || data_type == 'D') { + /* add string terminator to buffer */ + isc_buffer_putmem(&buf, "\0", 1); + put_data(isc_boolean_true, NULL, (char *) &data_arr); + } else if (data_type == 'c' || data_type == 'C') { + put_data(isc_boolean_false, (char *) &data_arr, + (char *) &data_arr2); + } else if (data_type == 'b') { + fprintf(stderr, "Bad / unknown token encountered on line %lu."\ + " Skipping line.", isc_lex_getsourceline(lexer) - 1); + } else { + fprintf(stderr, "Bad / unknown db data type encountered on " \ + "line %lu. Skipping line\n", isc_lex_getsourceline(lexer) - 1); + } + } + + if (token.type == isc_tokentype_eof) { + loop = isc_boolean_false; + } + + /* reset buffer for next insert */ + isc_buffer_clear(&buf); + isc_buffer_clear(&buf2); + have_czone = isc_boolean_false; + data_type ='u'; + break; + default: + data_type = 'b'; + break; + } + } + + return; + + data_cleanup: + /* let user know we had problems */ + fprintf(stderr, "Unknown error processing tokens during \"add\" or " \ + "\"bulk\" operation.\nStoped processing on line %lu.", + isc_lex_getsourceline(lexer)); +} + + +isc_result_t +openBDB(void) { + + int bdbres; + isc_result_t result; + + /* create BDB environment */ + /* Basically BDB allocates and assigns memory to db->dbenv */ + bdbres = db_env_create(&db.dbenv, 0); + if (bdbres != 0) { + fprintf(stderr, "BDB environment could not be created. BDB error: %s", + db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + /* open BDB environment */ + if (create_allowed == isc_boolean_true) { + /* allowed to create new files */ + bdbres = db.dbenv->open(db.dbenv, db_envdir, + DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE, 0); + } else { /* not allowed to create new files. */ + bdbres = db.dbenv->open(db.dbenv, db_envdir, + DB_INIT_CDB | DB_INIT_MPOOL, 0); + } + if (bdbres != 0) { + fprintf(stderr, "BDB environment at '%s' could not be opened. BDB " \ + "error: %s", db_envdir, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + /* open dlz_data database. */ + + result = bdb_opendb(DB_RECNO, &db.data, dlz_data, 0); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* open dlz_host database */ + result = bdb_opendb(DB_BTREE, &db.host, dlz_host, DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* open dlz_zone database. */ + result = bdb_opendb(DB_BTREE, &db.zone, dlz_zone, DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* open dlz_client database. */ + result = bdb_opendb(DB_BTREE, &db.client, dlz_client, DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto openBDB_cleanup; + + /* associate the host secondary database with the primary database */ + bdbres = db.data->associate(db.data, NULL, db.host, gethost, 0); + if (bdbres != 0) { + fprintf(stderr, "BDB could not associate %s database with %s. BDB "\ + "error: %s", dlz_host, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + /* associate the zone secondary database with the primary database */ + bdbres = db.data->associate(db.data, NULL, db.zone, getzone, 0); + if (bdbres != 0) { + fprintf(stderr, "BDB could not associate %s database with %s. BDB "\ + "error: %s", dlz_zone, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto openBDB_cleanup; + } + + return result; + + openBDB_cleanup: + + bdb_cleanup(); + return result; +} + +/*% Create & open lexer to parse input data */ + +isc_result_t +open_lexer(void) { + isc_result_t result; + + /* check if we already opened the lexer, if we did, return success */ + if (lexer != NULL) + return ISC_R_SUCCESS; + + /* allocate memory for lexer, and verify it was allocated */ + result = isc_mem_create(0, 0, &lex_mctx); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error creating lexer\n"); + return result; + } + + /* create lexer */ + result = isc_lex_create(lex_mctx, 1500, &lexer); + if (result != ISC_R_SUCCESS) + fprintf(stderr, "unexpected error creating lexer\n"); + + /* set allowed commenting style */ + isc_lex_setcomments(lexer, ISC_LEXCOMMENT_C | /* Allow C comments */ + ISC_LEXCOMMENT_CPLUSPLUS | /* Allow C++ comments */ + ISC_LEXCOMMENT_SHELL); /* Allow shellcomments */ + + isc_buffer_init(&lex_buffer, &lex_data_buf, max_data_len); + + return result; +} + +/*% Close the lexer, and cleanup memory */ + +void +close_lexer(void) { + + /* If lexer is still open, close it & destroy it. */ + if (lexer != NULL) { + isc_lex_close(lexer); + isc_lex_destroy(&lexer); + } + + /* if lexer memory is still allocated, destroy it. */ + if (lex_mctx != NULL) + isc_mem_destroy(&lex_mctx); +} + +/*% Perform add operation */ + +void +operation_add(void) { + /* check for any parameters that are not allowed during add */ + checkInvalidParam(key, "k", "for add operation"); + checkInvalidParam(zone, "z", "for add operation"); + checkInvalidParam(host, "h", "for add operation"); + checkInvalidParam(c_zone, "c", "for add operation"); + checkInvalidParam(c_ip, "i", "for add operation"); + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for add operation"); + + /* if open lexer fails it alread prints error messages. */ + if (open_lexer() != ISC_R_SUCCESS) { + quit(4); + } + + /* copy input data to buffer */ + isc_buffer_putstr(&lex_buffer, a_data); + + /* tell lexer to use buffer as input */ + if (isc_lex_openbuffer(lexer, &lex_buffer) != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error opening lexer buffer"); + quit(4); + } + + /*common logic for "add" & "bulk" operations are handled by insert_data */ + insert_data(); +} + +/*% Perform bulk insert operation */ + +void +operation_bulk(void) { + /* check for any parameters that are not allowed during bulk */ + checkInvalidParam(key, "k", "for bulk load operation"); + checkInvalidParam(zone, "z", "for bulk load operation"); + checkInvalidParam(host, "h", "for bulk load operation"); + checkInvalidParam(c_zone, "c", "for bulk load operation"); + checkInvalidParam(c_ip, "i", "for bulk load operation"); + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for bulk load operation"); + + /* if open lexer fails it already prints error messages. */ + if (open_lexer() != ISC_R_SUCCESS) { + quit(4); + } + + if (bulk_file == NULL) { + if (isc_lex_openstream(lexer, stdin) != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error opening stdin by lexer."); + quit(4); + } + } else if (isc_lex_openfile(lexer, bulk_file) != ISC_R_SUCCESS) { + fprintf(stderr, "unexpected error opening %s by lexer.", bulk_file); + quit(4); + } + + /* common logic for "add" & "bulk" operations are handled by insert_data */ + insert_data(); +} + +isc_result_t +bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata) { + + int bdbres; + db_recno_t recNum; + char *retkey = NULL, *retdata; + size_t retklen = 0, retdlen; + void *p; + + /* use a 5MB buffer for the bulk dump */ + int buffer_size = 5 * 1024 * 1024; + + /* try to allocate a 5 MB buffer, if we fail write err msg, die. */ + bdbdata->data = malloc(buffer_size); + if (bdbdata->data == NULL) { + fprintf(stderr, + "Unable to allocate 5 MB buffer for bulk database dump\n"); + return ISC_R_FAILURE; + } + bdbdata->ulen = buffer_size; + bdbdata->flags = DB_DBT_USERMEM; + + /* get a cursor, make sure it worked. */ + bdbres = database->cursor(database, NULL, &dbcursor, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n",db_strerror(bdbres)); + free(bdbdata->data); + return ISC_R_FAILURE; + } + + /* loop and dump all data */ + for (;;) { + + /* loop through data until DB_NOTFOUND is returned */ + bdbres = dbcursor->c_get(dbcursor, bdbkey, bdbdata, + DB_MULTIPLE_KEY | DB_NEXT); + /* if not successful did we encounter DB_NOTFOUND, or */ + /* have a different problem. */ + if (bdbres != 0) { + if (bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + free(bdbdata->data); + return ISC_R_FAILURE; + } + /* Hit DB_NOTFOUND which means end of data. */ + break; + } /* end of if (bdbres !=0) */ + + for (DB_MULTIPLE_INIT(p, bdbdata);;) { + if (type == 'c') + DB_MULTIPLE_KEY_NEXT(p, bdbdata, retkey, retklen, retdata, retdlen); + else + DB_MULTIPLE_RECNO_NEXT(p, bdbdata, recNum, retdata, retdlen); + + if (p == NULL) + break; + if (type == 'c') + printf("c %.*s %.*s\n",(int)retklen, retkey,(int)retdlen, retdata); + else + printf("d %.*s\n", (int)retdlen, retdata); + } /* end of for (DB_MULTIPLE_INIT....) */ + + } /* end of for (;;) */ + + /* free the buffer we created earlier */ + free(bdbdata->data); + + return ISC_R_SUCCESS; +} + +/*% + * Perform listOrDelete operation + * if dlt == true, delete data + * else list data + */ + +void +operation_listOrDelete(isc_boolean_t dlt) { + + int bdbres = 0; + DBC *curList[3]; + DBT bdbkey, bdbdata; + db_recno_t recno; + int curIndex = 0; + + + /* verify that only allowed parameters were passed. */ + if (dlt == isc_boolean_true) { + checkInvalidParam(zone, "z", "for delete operation"); + checkInvalidParam(host, "h", "for delete operation"); + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for delete operation"); + checkInvalidOption(create_allowed, isc_boolean_true, "n", + "for delete operation"); + } else if (key != NULL || zone != NULL || host != NULL) { + checkInvalidParam(c_zone, "c", "for list when k, z or h are specified"); + checkInvalidParam(c_ip, "i", "for list when k, z, or h are specified"); + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for list when k, z, or h are specified"); + checkInvalidOption(create_allowed, isc_boolean_true, "n", + "for list operation"); + } else if (c_ip != NULL || c_zone != NULL) { + checkInvalidOption(list_everything, isc_boolean_true, "e", + "for list when c or i are specified"); + checkInvalidOption(create_allowed, isc_boolean_true, "n", + "for list operation"); + } + + memset(&bdbkey, 0, sizeof(bdbkey)); + memset(&bdbdata, 0, sizeof(bdbdata)); + + /* Dump database in "dlzbdb" bulk format */ + if (list_everything == isc_boolean_true) { + if (bulk_write('c', db.client, db.cursor, &bdbkey, &bdbdata) + != ISC_R_SUCCESS) + return; + memset(&bdbkey, 0, sizeof(bdbkey)); + memset(&bdbdata, 0, sizeof(bdbdata)); + bulk_write('d', db.data, db.cursor2, &bdbkey, &bdbdata); + return; + } /* end if (list_everything) */ + + /* set NULL the 2nd and 3rd positions in curList. */ + /* that way later when add cursors to the join list */ + /* it is already null terminated. */ + curList[1] = curList[2] = NULL; + + if (key != NULL) { + /* make sure other parameters weren't */ + checkInvalidParam(zone, "z", "when k is specified"); + checkInvalidParam(host, "h", "when k is specified"); + + recno = key_val; + bdbkey.data = &recno; + bdbkey.size = sizeof(recno); + + if (dlt == isc_boolean_true) { + bdbres = db.data->del(db.data, NULL, &bdbkey, 0); + } else { + bdbdata.flags = DB_DBT_REALLOC; + bdbres = db.data->get(db.data, NULL, &bdbkey, &bdbdata, 0); + + if (bdbres == 0) { + printf("KEY | DATA\n"); + printf("%lu | %.*s\n", *(u_long *) bdbkey.data, + (int)bdbdata.size, (char *)bdbdata.data); + } + } /* closes else of if (dlt == isc_boolean_true) */ + if (bdbres == DB_NOTFOUND) { + printf("Key not found in database"); + } + } /* closes if (key != NULL) */ + + /* if zone is passed */ + if (zone != NULL) { + /* create a cursor and make sure it worked */ + bdbres = db.zone->cursor(db.zone, NULL, &db.cursor2, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + return; + } + + bdbkey.data = zone; + bdbkey.size = strlen(zone); + bdbres = db.cursor2->c_get(db.cursor2, &bdbkey, &bdbdata, DB_SET); + if (bdbres != 0) { + if (bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + } else { + printf("Zone not found in database"); + } + return; + } + + /* add cursor to cursor list for later use in join */ + curList[curIndex++] = db.cursor2; + } + + /* if host is passed */ + if (host != NULL) { + + /* create a cursor and make sure it worked. */ + bdbres = db.host->cursor(db.host, NULL, &db.cursor3, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + return; + } + bdbkey.data = host; + bdbkey.size = strlen(host); + bdbres = db.cursor3->c_get(db.cursor3, &bdbkey, &bdbdata, DB_SET); + if (bdbres != 0) { + if (bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + } else { + printf("Host not found in database"); + } + return; + } + + /* add cursor to cursor list for later use in join */ + curList[curIndex++] = db.cursor3; + } + + + if (zone != NULL || host != NULL) { + + /* join any cursors */ + bdbres = db.data->join(db.data, curList, &db.cursor4, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + return; + } + + memset(&bdbkey, 0, sizeof(bdbkey)); + bdbkey.flags = DB_DBT_REALLOC; + memset(&bdbdata, 0, sizeof(bdbdata)); + bdbdata.flags = DB_DBT_REALLOC; + + /* print a header to explain the output */ + printf("KEY | DATA\n"); + /* loop and list all results. */ + while (bdbres == 0) { + /* get data */ + bdbres = db.cursor4->c_get(db.cursor4, &bdbkey, &bdbdata, 0); + /* verify call had no errors */ + if (bdbres != 0) { + break; + } + printf("%lu | %.*s\n", *(u_long *) bdbkey.data, + (int)bdbdata.size, (char *)bdbdata.data); + } /* closes while loop */ + } + + if (c_ip != NULL && c_zone == NULL) { + fprintf(stderr, "i may only be specified when c is also specified\n"); + quit(2); + } + /* if client_zone was passed */ + if (c_zone != NULL) { + + /* create a cursor and make sure it worked. */ + if (dlt == isc_boolean_true) { + /* open read-write cursor */ + bdbres = db.client->cursor(db.client, NULL, &db.cursor, + DB_WRITECURSOR); + } else { + /* open read only cursor */ + bdbres = db.client->cursor(db.client, NULL, &db.cursor, 0); + /* print a header to explain the output */ + printf("CLIENT_ZONE | CLIENT_IP\n"); + } + + bdbkey.data = c_zone; + bdbkey.size = strlen(c_zone); + + if (c_ip != NULL) { + bdbdata.data = c_ip; + bdbdata.size = strlen(c_ip); + bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_GET_BOTH); + if (bdbres == DB_NOTFOUND) { + printf("Client zone & IP not found in database"); + } + } else { + bdbdata.flags = DB_DBT_REALLOC; + bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_SET); + if (bdbres == DB_NOTFOUND) { + printf("Client zone not found in database"); + } + } + + while (bdbres == 0) { + if (dlt == isc_boolean_false) { + printf("%.*s | %.*s\n", (int)bdbkey.size, (char *) bdbkey.data, + (int)bdbdata.size, (char *) bdbdata.data); + } else { + /* delete record. */ + bdbres = db.cursor->c_del(db.cursor, 0); + if (bdbres != 0) { + fprintf(stderr, "Unexpected error. BDB Error: %s\n", + db_strerror(bdbres)); + break; + } + } + if (c_ip != NULL) { + break; + } + bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_NEXT_DUP); + if (bdbres != 0) { + break; + } + } /* end while loop */ + } + + + if (bdbres != 0 && bdbres != DB_NOTFOUND) { + fprintf(stderr, "Unexpected error during list operation " \ + "BDB error: %s", db_strerror(bdbres)); + } + + if (bdbkey.flags == DB_DBT_REALLOC && bdbkey.data != NULL) { + free(bdbkey.data); + } + if (bdbdata.flags == DB_DBT_REALLOC && bdbdata.data != NULL) { + free(bdbdata.data); + } +} + + +int +main(int argc, char **argv) { + + int ch; + char *endp; + + /* there has to be at least 2 args, some operations require more */ + if (argc < 2) + show_usage(); + + /* use the ISC commandline parser to get all the program arguments */ + while ((ch= isc_commandline_parse(argc, argv, "ldesna:f:k:z:h:c:i:")) != -1) { + switch (ch) { + case 'n': + create_allowed = isc_boolean_true; + break; + case 'l': + checkOp(operation); + operation = list; + break; + case 'd': + checkOp(operation); + operation = dele; + break; + case 'a': + checkOp(operation); + operation = add; + a_data = isc_commandline_argument; + break; + case 'f': + checkOp(operation); + operation = bulk; + bulk_file = isc_commandline_argument; + break; + case 's': + checkOp(operation); + operation = bulk; + break; + case 'k': + checkParam(key, "k"); + key = isc_commandline_argument; + key_val = strtoul(key, &endp, 10); + if (*endp != '\0' || key_val < 1) { + fprintf(stderr, "Error converting key to integer"); + } + break; + case 'z': + checkParam(zone, "z"); + zone = isc_commandline_argument; + break; + case 'h': + checkParam(host, "h"); + host = isc_commandline_argument; + break; + case 'c': + checkParam(c_zone, "c"); + c_zone = isc_commandline_argument; + break; + case 'i': + checkParam(c_ip, "i"); + c_ip = isc_commandline_argument; + break; + case 'e': + checkOp(operation); + operation = list; + list_everything = isc_boolean_true; + break; + case '?': + show_usage(); + break; + default: + /* should never reach this point */ + fprintf(stderr, "unexpected error parsing command arguments\n"); + quit(1); + break; + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + /* argc & argv have been modified, so now only "extra" parameters are */ + /* left in argc & argv. "Extra" parameters are any parameters that were */ + /* not passed using a command line flag. Exactly 2 args should be left. */ + /* The first should be the BDB environment path, the second should be the */ + /* BDB database. The BDB database path can be either relative to the */ + /* BDB environment path, or absolute. */ + if (argc < 2) { + fprintf(stderr, "Both a Berkeley DB environment and file "\ + "must be specified"); + quit(2); + } else if (argc > 2) { + fprintf(stderr, "Too many parameters. Check command line for errors."); + quit(2); + } + + /* get db_file to operate on */ + db_envdir = argv[0]; + db_file = argv[1]; + + if (openBDB() != ISC_R_SUCCESS) { + /* openBDB already prints error messages, don't do it here. */ + bdb_cleanup(); + quit(3); + } + + switch(operation) { + case list: + operation_listOrDelete(isc_boolean_false); + break; + case dele: + operation_listOrDelete(isc_boolean_true); + break; + case add: + operation_add(); + break; + case bulk: + operation_bulk(); + break; + default: + fprintf(stderr, "\nNo operation was selected. "\ + "Select an operation (l d a f)"); + quit(2); + break; + } + + quit(0); +} +#endif + diff --git a/contrib/dlz/config.dlz.in b/contrib/dlz/config.dlz.in new file mode 100644 index 00000000000..af463eb21c8 --- /dev/null +++ b/contrib/dlz/config.dlz.in @@ -0,0 +1,434 @@ +# Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# +# Shorthand. Note quoting: DLZ_DRIVER_DIR expanded in Makefile, not here. +# +dlzdir='${DLZ_DRIVER_DIR}' + +# +# Private autoconf macro to simplify configuring drivers: +# +# DLZ_ADD_DRIVER(DEFINE, DRIVER, INCLUDES, LIBS) +# +# where: +# DEFINE is FOO (to define -DDLZ_FOO) +# DRIVER is dlz_foo_driver (sources without the .c) +# INCLUDES is any necessary include definitions +# LIBS is any necessary library definitions +# +AC_DEFUN(DLZ_ADD_DRIVER, [ + USE_DLZ="$USE_DLZ -DDLZ_$1" + for i in $2 + do + DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c" + DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O" + done + if test -n "$3" + then + DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES $3" + fi + if test -n "$4" + then + DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS $4" + fi +]) + +# +# Check for the various DLZ drivers +# + +# +# Was --with-dlz-postgres specified? +# + +AC_MSG_CHECKING(for Postgres DLZ driver) +AC_ARG_WITH(dlz_postgres, +[ --with-dlz-postgres[=PATH] Build with Postgres DLZ driver [yes|no|path]. + (Required to use Postgres with DLZ)], + use_dlz_postgres="$withval", use_dlz_postgres="no") + +if test "$use_dlz_postgres" = "yes" +then + # User did not specify a path - guess it + # Ask Postgres to tell us where it is + + AC_PATH_PROGS(PG_CONFIG, pg_config, [not found]) + + if test "$PG_CONFIG" != "not found" + then + use_dlz_postgres=`$PG_CONFIG --includedir` + use_dlz_postgres_lib=`$PG_CONFIG --libdir` + fi +fi + +if test "$use_dlz_postgres" = "yes" +then + # User did not specify path and Postgres didn't say - guess it + + pgdirs="/usr /usr/local /usr/local/pgsql /usr/pkg" + for d in $pgdirs + do + if test -f $d/include/libpq-fe.h + then + use_dlz_postgres=$d/include + use_dlz_postgres_lib=$d/lib + break + fi + done +fi + +if test "$use_dlz_postgres" = "yes" +then + # Still no joy, give up + + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[No pg_config and PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path or put pg_config in your path]) +fi + +case "$use_dlz_postgres" in + no) + AC_MSG_RESULT(no) + ;; + *) + DLZ_ADD_DRIVER(POSTGRES, dlz_postgres_driver, + [-I$use_dlz_postgres], + [-L$use_dlz_postgres_lib -lpq]) + + AC_MSG_RESULT( +[using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres]) + ;; +esac + + +# +# Was --with-dlz-mysql specified? +# + +AC_MSG_CHECKING(for MySQL DLZ driver) +AC_ARG_WITH(dlz_mysql, +[ --with-dlz-mysql[=PATH] Build with MySQL DLZ driver [yes|no|path]. + (Required to use MySQL with DLZ)], + use_dlz_mysql="$withval", use_dlz_mysql="no") + +if test "$use_dlz_mysql" = "yes" +then + # User did not specify a path - guess it + mysqldirs="/usr /usr/local /usr/local/mysql /usr/pkg" + for d in $mysqldirs + do + if test -f $d/include/mysql/mysql.h + then + use_dlz_mysql=$d + break + fi + done +fi + +if test "$use_dlz_mysql" = "yes" +then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path]) +fi + +case "$use_dlz_mysql" in + no) + AC_MSG_RESULT(no) + ;; + *) + DLZ_ADD_DRIVER(MYSQL, dlz_mysql_driver, + [-I$use_dlz_mysql/include/mysql], + [-L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lm]) + + AC_MSG_RESULT( +[using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql]) + ;; +esac + + +# +# Was --with-dlz-bdb specified? +# + +AC_MSG_CHECKING(for Berkeley DB DLZ driver) +AC_ARG_WITH(dlz_bdb, +[ --with-dlz-bdb[=PATH] Build with Berkeley DB DLZ driver [yes|no|path]. + (Required to use Berkeley DB with DLZ)], + use_dlz_bdb="$withval", use_dlz_bdb="no") + +case "$use_dlz_bdb" in + no) + AC_MSG_RESULT(no) + ;; + *) + if test "$use_dlz_bdb" = "yes" + then + # User did not specify a path - guess directories + bdbdirs="/usr/local /usr/pkg /usr" + elif test -d "$use_dlz_bdb" + then + # User specified directory and it exists + bdbdirs="$use_dlz_bdb" + else + AC_MSG_RESULT(not found) + AC_MSG_ERROR([path $use_dlz_bdb does not exist]) + bdbdirs="" + fi + + # Use path we were given or guessed. This is insanely + # complicated because we have to search for a bunch of + # platform-specific variations and have to check + # separately for include and library directories. + + # Set both to yes, so we can check them later + dlz_bdb_inc="yes" + dlz_bdb_libs="yes" + + for dd in $bdbdirs + do + # Skip nonexistant directories + if test ! -d "$dd" + then + continue + fi + + # Check other locations for includes. + # Order is important (sigh). + + bdb_incdirs="/ /db42/ /db41/ /db4/ /db/" + for d in $bdb_incdirs + do + if test -f "$dd/include${d}db.h" + then + dlz_bdb_inc="-I$dd/include${d}" + break + fi + done + + # Give up on this directory if we couldn't + # find the include subdir + + if test "$dlz_bdb_inc" = "yes" + then + continue + fi + + # Look for libname other than libdb.so. + # Order is important (sigh). + + bdb_libnames="db42 db-4.2 db41 db-4.1 db" + for d in $bdb_libnames + do + if test -f "$dd/lib/lib${d}.so" + then + if test "$dd" != "/usr" + then + dlz_bdb_libs="-L${dd}/lib " + fi + dlz_bdb_libs="${dlz_bdb_libs}-l${d}" + break + fi + done + + # If we found both incdir and lib, we're done + if test "$dlz_bdb_libs" != "yes" + then + break + fi + + # Otherwise, we're starting over + + dlz_bdb_inc="yes" + dlz_bdb_libs="yes" + done + + # Done searching, now make sure we got everything. + + if test "$dlz_bdb_inc" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR([could not find Berkeley DB include directory]) + fi + + if test "$dlz_bdb_libs" = "yes" + then + AC_MSG_RESULT(not found) + AC_MSG_ERROR([could not find Berkeley DB library]) + fi + + DLZ_ADD_DRIVER(BDB, dlz_bdb_driver dlz_bdbhpt_driver, + [$dlz_bdb_inc], [$dlz_bdb_libs]) + + AC_MSG_RESULT([using Berkeley DB: $dlz_bdb_inc $dlz_bdb_libs]) + + AC_CONFIG_FILES([contrib/dlz/bin/dlzbdb/Makefile]) + ;; +esac + + +# +# Was --with-dlz-filesystem specified? +# + +AC_MSG_CHECKING(for file system DLZ driver) +AC_ARG_WITH(dlz_filesystem, +[ --with-dlz-filesystem[=PATH] Build with filesystem DLZ driver [yes|no]. + (Required to use file system driver with DLZ)], + use_dlz_filesystem="$withval", use_dlz_filesystem="no") + +case "$use_dlz_filesystem" in + no) + AC_MSG_RESULT(no) + ;; + *) + DLZ_ADD_DRIVER(FILESYSTEM, dlz_filesystem_driver) + + AC_MSG_RESULT(yes) + ;; +esac + + +# +# Was --with-dlz-ldap specified? +# + +AC_MSG_CHECKING(for LDAP DLZ driver) +AC_ARG_WITH(dlz_ldap, +[ --with-dlz-ldap[=PATH] Build with LDAP DLZ driver [yes|no|path]. + (Required to use LDAP with DLZ)], + use_dlz_ldap="$withval", use_dlz_ldap="no") + +if test "$use_dlz_ldap" = "yes" +then + # User did not specify a path - guess it + ldapdirs="/usr /usr/local /usr/pkg" + for d in $ldapdirs + do + if test -f $d/include/ldap.h + then + use_dlz_ldap=$d + break + fi + done +fi + +if test "$use_dlz_ldap" = "yes" +then + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path]) +fi + +case "$use_dlz_ldap" in + no) + AC_MSG_RESULT(no) + ;; + *) + DLZ_ADD_DRIVER(LDAP, dlz_ldap_driver, + [-I$use_dlz_ldap/include], + [-L$use_dlz_ldap/lib -lldap -llber]) + + AC_MSG_RESULT( +[using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include]) + ;; +esac + + +# +# Was --with-dlz-odbc specified? +# + +AC_MSG_CHECKING(for ODBC DLZ driver) +AC_ARG_WITH(dlz_odbc, +[ --with-dlz-odbc[=PATH] Build with ODBC DLZ driver [yes|no|path]. + (Required to use ODBC with DLZ)], + use_dlz_odbc="$withval", use_dlz_odbc="no") + +if test "$use_dlz_odbc" = "yes" +then + # User did not specify a path - guess it + odbcdirs="/usr /usr/local /usr/pkg" + for d in $odbcdirs + do + if test -f $d/include/sql.h -a -f $d/lib/libodbc.a + then + use_dlz_odbc=$d + break + fi + done +fi + +case "$use_dlz_odbc" in + no) + AC_MSG_RESULT(no) + ;; + yes) + AC_MSG_RESULT(not found) + AC_MSG_ERROR( +[ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path]) + ;; + *) + DLZ_ADD_DRIVER(ODBC, dlz_odbc_driver, + [-I$use_dlz_odbc/include], + [-L$use_dlz_odbc/lib -lodbc]) + + AC_MSG_RESULT([using ODBC from $use_dlz_odbc]) + ;; +esac + + +# +# Was --with-dlz-stub specified? +# + +AC_MSG_CHECKING(for stub DLZ driver) +AC_ARG_WITH(dlz_stub, +[ --with-dlz-stub[=PATH] Build with stub DLZ driver [yes|no]. + (Required to use stub driver with DLZ)], + use_dlz_stub="$withval", use_dlz_stub="no") + +case "$use_dlz_stub" in + no) + AC_MSG_RESULT(no) + ;; + *) + + DLZ_ADD_DRIVER(STUB, dlz_stub_driver) + + AC_MSG_RESULT(yes) + ;; +esac + + +# Add any additional DLZ drivers here. + +# +# Finally, some generic stuff that applies to all drivers, assuming +# we're compiling DLZ at all. +# +if test -n "$USE_DLZ" +then + # + # Where to find DLZ driver header files. + # + DLZ_DRIVER_INCLUDES="-I$dlzdir/include $DLZ_DRIVER_INCLUDES" + + # + # Initialization and shutdown wrappers, helper functions. + # + DLZ_DRIVER_SRCS="$dlzdir/dlz_drivers.c $dlzdir/sdlz_helper.c $DLZ_DRIVER_SRCS" + DLZ_DRIVER_OBJS="dlz_drivers.$O sdlz_helper.$O $DLZ_DRIVER_OBJS" +fi diff --git a/contrib/dlz/drivers/.cvsignore b/contrib/dlz/drivers/.cvsignore new file mode 100644 index 00000000000..de7475d8888 --- /dev/null +++ b/contrib/dlz/drivers/.cvsignore @@ -0,0 +1 @@ +rules diff --git a/contrib/dlz/drivers/dlz_bdb_driver.c b/contrib/dlz/drivers/dlz_bdb_driver.c new file mode 100644 index 00000000000..5aa96a59ffc --- /dev/null +++ b/contrib/dlz/drivers/dlz_bdb_driver.c @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_BDB + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +static dns_sdlzimplementation_t *dlz_bdb = NULL; + +/* should the bdb driver use threads. */ +#ifdef ISC_PLATFORM_USETHREADS +#define bdb_threads DB_THREAD +#else +#define bdb_threads 0 +#endif + +/* BDB database names */ +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_host "dns_host" +#define dlz_client "dns_client" + +/*% + * This structure contains all the Berkeley DB handles + * for this instance of the BDB driver. + */ + +typedef struct bdb_instance { + DB_ENV *dbenv; /*%< BDB environment */ + DB *data; /*%< dns_data database handle */ + DB *zone; /*%< zone database handle */ + DB *host; /*%< host database handle */ + DB *client; /*%< client database handle */ + isc_mem_t *mctx; /*%< memory context */ + +} bdb_instance_t; + +typedef struct parsed_data { + char *zone; + char *host; + char *type; + int ttl; + char *data; +} parsed_data_t; + + +/* forward reference */ + +static isc_result_t +bdb_findzone(void *driverarg, void *dbdata, const char *name); + +/*% + * Parses the DBT from the Berkeley DB into a parsed_data record + * The parsed_data record should be allocated before and passed into the + * bdb_parse_data function. The char (type & data) fields should not + * be "free"d as that memory is part of the DBT data field. It will be + * "free"d when the DBT is freed. + */ + +static isc_result_t +bdb_parse_data(char *in, parsed_data_t *pd) { + + char *endp, *ttlStr; + char *tmp = in; + char *lastchar = (char *) &tmp[strlen(tmp) + 1]; + + /*% + * String should be formated as: + * zone(a space)host(a space)ttl(a space)type(a space)remaining data + * examples: + * example.com www 10 A 127.0.0.1 + * example.com mail 10 A 127.0.0.2 + * example.com @ 10 MX 20 mail.example.com + */ + + /* save pointer to zone */ + pd->zone = tmp; + + /* find space after zone and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to host */ + pd->host = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to dns type */ + pd->type = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to dns ttl */ + ttlStr = tmp; + + /* find space after ttl and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to remainder of DNS data */ + pd->data = tmp; + + /* convert ttl string to integer */ + pd->ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || pd->ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB driver ttl must be a postive number"); + return ISC_R_FAILURE; + } + + /* if we get this far everything should have worked. */ + return ISC_R_SUCCESS; +} + +/* + * DLZ methods + */ + +static isc_result_t +bdb_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *client_cursor = NULL; + DBT key, data; + + /* check to see if we are authoritative for the zone first. */ + result = bdb_findzone(driverarg, dbdata, name); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + memset(&key, 0, sizeof(DBT)); + key.flags = DB_DBT_MALLOC; + key.data = strdup(name); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + key.size = strlen(key.data); + + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + data.data = strdup(client); + if (data.data == NULL) { + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + data.size = strlen(data.data); + + /* get a cursor to loop through zone data */ + if (db->client->cursor(db->client, NULL, &client_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto xfr_cleanup; + } + + switch(client_cursor->c_get(client_cursor, &key, &data, DB_GET_BOTH)) { + case DB_NOTFOUND: + case DB_SECONDARY_BAD: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + + xfr_cleanup: + + /* free any memory duplicate string in the key field */ + if (key.data != NULL) + free(key.data); + + /* free any memory allocated to the data field. */ + if (data.data != NULL) + free(data.data); + + /* get rid of zone_cursor */ + if (client_cursor != NULL) + client_cursor->c_close(client_cursor); + + return result; + +} + +static isc_result_t +bdb_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *zone_cursor = NULL; + DBT key, data; + int flags; + int bdbres; + parsed_data_t pd; + char *tmp = NULL, *tmp_zone; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data = tmp_zone = strdup(zone); + + if (key.data == NULL) + return (ISC_R_NOMEMORY); + + key.size = strlen(key.data); + + /* get a cursor to loop through zone data */ + if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + flags = DB_SET; + + while ((bdbres = zone_cursor->c_get(zone_cursor, &key, &data, + flags)) == 0) { + + flags = DB_NEXT_DUP; + + tmp = realloc(tmp, data.size + 1); + if (tmp == NULL) + goto allnodes_cleanup; + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if (bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto allnodes_cleanup; + + result = dns_sdlz_putnamedrr(allnodes, pd.host, pd.type, + pd.ttl, pd.data); + if (result != ISC_R_SUCCESS) + goto allnodes_cleanup; + + } /* end while loop */ + + allnodes_cleanup: + + if (tmp != NULL) + free(tmp); + + /* free any memory duplicate string in the key field */ + if (tmp_zone != NULL) + free(tmp_zone); + + /* get rid of zone_cursor */ + if (zone_cursor != NULL) + zone_cursor->c_close(zone_cursor); + + return result; + +} + +/*% + * Performs BDB cleanup. + * Used by bdb_create if there is an error starting up. + * Used by bdb_destroy when the driver is shutting down. + */ + +static void +bdb_cleanup(bdb_instance_t *db) { + + isc_mem_t *mctx; + + /* close databases */ + if (db->data != NULL) + db->data->close(db->data, 0); + if (db->host != NULL) + db->host->close(db->host, 0); + if (db->zone != NULL) + db->zone->close(db->zone, 0); + if (db->client != NULL) + db->client->close(db->client, 0); + + /* close environment */ + if (db->dbenv != NULL) + db->dbenv->close(db->dbenv, 0); + + /* cleanup memory */ + if (db->mctx != NULL) { + /* save mctx for later */ + mctx = db->mctx; + /* return, and detach the memory */ + isc_mem_put(mctx, db, sizeof(bdb_instance_t)); + isc_mem_detach(&mctx); + } +} + +static isc_result_t +bdb_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *zone_cursor = NULL; + DBT key, data; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + key.data = strdup(name); + + if (key.data == NULL) + return (ISC_R_NOMEMORY); + + key.size = strlen(key.data); + + /* get a cursor to loop through zone data */ + if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) { + result = ISC_R_NOTFOUND; + goto findzone_cleanup; + } + + switch(zone_cursor->c_get(zone_cursor, &key, &data, DB_SET)) { + case DB_NOTFOUND: + case DB_SECONDARY_BAD: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + + findzone_cleanup: + + /* free any memory duplicate string in the key field */ + if (key.data != NULL) + free(key.data); + + /* free any memory allocated to the data field. */ + if (data.data != NULL) + free(data.data); + + /* get rid of zone_cursor */ + if (zone_cursor != NULL) + zone_cursor->c_close(zone_cursor); + + return result; +} + +static isc_result_t +bdb_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdb_instance_t *db = (bdb_instance_t *) dbdata; + DBC *zone_cursor = NULL; + DBC *host_cursor = NULL; + DBC *join_cursor = NULL; + DBT key, data; + DBC *cur_arr[3]; + int bdbres; + parsed_data_t pd; + char *tmp_zone, *tmp_host = NULL; + char *tmp = NULL; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + /* set zone key */ + key.data = tmp_zone = strdup(zone); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto lookup_cleanup; + } + key.size = strlen(key.data); + + /* get a cursor to loop through zone data */ + if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + /* initialize zone_cursor with zone_key */ + if (zone_cursor->c_get(zone_cursor, &key, &data, DB_SET) != 0) { + result = ISC_R_NOTFOUND; + goto lookup_cleanup; + } + + /* set host key */ + key.data = tmp_host = strdup(name); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto lookup_cleanup; + } + key.size = strlen(key.data); + + /* get a cursor to loop through host data */ + if (db->host->cursor(db->host, NULL, &host_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + /* initialize host_cursor with host_key */ + if (host_cursor->c_get(host_cursor, &key, &data, DB_SET) != 0) { + result = ISC_R_NOTFOUND; + goto lookup_cleanup; + } + + cur_arr[0] = zone_cursor; + cur_arr[1] = host_cursor; + cur_arr[2] = NULL; + + db->data->join(db->data, cur_arr, &join_cursor, 0); + + while ((bdbres = join_cursor->c_get(join_cursor, &key, + &data, 0)) == 0) { + + tmp = realloc(tmp, data.size + 1); + if (tmp == NULL) + goto lookup_cleanup; + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if (bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto lookup_cleanup; + + result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data); + + if (result != ISC_R_SUCCESS) + goto lookup_cleanup; + } /* end while loop */ + + lookup_cleanup: + + if (tmp != NULL) + free(tmp); + if (tmp_zone != NULL) + free(tmp_zone); + if (tmp_host != NULL) + free(tmp_host); + + /* get rid of the joined cusor */ + if (join_cursor != NULL) + join_cursor->c_close(join_cursor); + + /* get rid of zone_cursor */ + if (zone_cursor != NULL) + zone_cursor->c_close(zone_cursor); + + /* get rid of host_cursor */ + if (host_cursor != NULL) + host_cursor->c_close(host_cursor); + + return result; + + return ISC_R_NOTFOUND; +} + + +/*% Initializes, sets flags and then opens Berkeley databases. */ + +static isc_result_t +bdb_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags) { + + int result; + + /* Initialize the database. */ + if ((result = db_create(db, db_env, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not initialize %s database. " + "BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* set database flags. */ + if ((result = (*db)->set_flags(*db, flags)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not set flags for %s database. " + "BDB error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* open the database. */ + if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type, + DB_RDONLY | bdb_threads, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not open %s database in %s. " + "BDB error: %s", + db_name, db_file, db_strerror(result)); + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +static isc_result_t +bdb_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + int bdbres; + bdb_instance_t *db = NULL; + + UNUSED(dlzname); + UNUSED(driverarg); + + /* verify we have 3 arg's passed to the driver */ + if (argc != 3) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Berkeley DB driver requires at least " + "2 command line args."); + return (ISC_R_FAILURE); + } + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(ns_g_mctx, sizeof(bdb_instance_t)); + if (db == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for " + "database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(bdb_instance_t)); + + /* attach to the memory context */ + isc_mem_attach(ns_g_mctx, &db->mctx); + + /* create BDB environment + * Basically BDB allocates and assigns memory to db->dbenv + */ + bdbres = db_env_create(&db->dbenv, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB environment could not be created. " + "BDB error: %s", + db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open BDB environment */ + bdbres = db->dbenv->open(db->dbenv, argv[1], + DB_INIT_CDB | DB_INIT_MPOOL | + bdb_threads | DB_CREATE, + 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB environment at '%s' could not be opened. " + "BDB error: %s", + argv[1], db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open dlz_data database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->data, + dlz_data, argv[2], 0); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_host database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->host, + dlz_host, argv[2], + DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_zone database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->zone, + dlz_zone, argv[2], + DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_client database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->client, + dlz_client, argv[2], DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* associate the host secondary database with the primary database */ + bdbres = db->data->associate(db->data, NULL, db->host, NULL, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not associate %s database with %s. " + "BDB error: %s", + dlz_host, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* associate the zone secondary database with the primary database */ + bdbres = db->data->associate(db->data, NULL, db->zone, NULL, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not associate %s database with %s. " + "BDB error: %s", + dlz_zone, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + *dbdata = db; + + return(ISC_R_SUCCESS); + + init_cleanup: + + bdb_cleanup(db); + return result; +} + +static void +bdb_destroy(void *driverarg, void *dbdata) +{ + UNUSED(driverarg); + + bdb_cleanup((bdb_instance_t *) dbdata); +} + +/* bdb_authority not needed as authority data is returned by lookup */ +static dns_sdlzmethods_t dlz_bdb_methods = { + bdb_create, + bdb_destroy, + bdb_findzone, + bdb_lookup, + NULL, + bdb_allnodes, + bdb_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_bdb_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ bdb driver."); + + result = dns_sdlzregister("bdb", &dlz_bdb_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + ns_g_mctx, &dlz_bdb); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_bdb_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ bdb driver."); + + if (dlz_bdb != NULL) + dns_sdlzunregister(&dlz_bdb); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_bdbhpt_driver.c b/contrib/dlz/drivers/dlz_bdbhpt_driver.c new file mode 100644 index 00000000000..da5b235bb1a --- /dev/null +++ b/contrib/dlz/drivers/dlz_bdbhpt_driver.c @@ -0,0 +1,860 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_BDB + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +static dns_sdlzimplementation_t *dlz_bdbhpt = NULL; + +/* should the bdb driver use threads. */ +#ifdef ISC_PLATFORM_USETHREADS +#define bdbhpt_threads DB_THREAD +#else +#define bdbhpt_threads 0 +#endif + +/* bdbhpt database names */ +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_xfr "dns_xfr" +#define dlz_client "dns_client" + + /* This structure contains all the Berkeley DB handles + * for this instance of the bdbhpt driver. + */ + +typedef struct bdbhpt_instance { + DB_ENV *dbenv; /*%< bdbhpt environment */ + DB *data; /*%< dns_data database handle */ + DB *zone; /*%< zone database handle */ + DB *xfr; /*%< zone xfr database handle */ + DB *client; /*%< client database handle */ + isc_mem_t *mctx; /*%< memory context */ + +} bdbhpt_instance_t; + +typedef struct bdbhpt_parsed_data { + char *host; + char *type; + int ttl; + char *data; +} bdbhpt_parsed_data_t; + + +/* forward reference */ + +static isc_result_t +bdbhpt_findzone(void *driverarg, void *dbdata, const char *name); + +/*% + * Reverses a string in place. + */ + +static char *bdbhpt_strrev(char *str) +{ + char *p1, *p2; + + if (! str || ! *str) + return str; + for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) + { + *p1 ^= *p2; + *p2 ^= *p1; + *p1 ^= *p2; + } + return str; +} + +/*% + * Parses the DBT from the Berkeley DB into a parsed_data record + * The parsed_data record should be allocated before and passed into the + * bdbhpt_parse_data function. The char (type & data) fields should not + * be "free"d as that memory is part of the DBT data field. It will be + * "free"d when the DBT is freed. + */ + +static isc_result_t +bdbhpt_parse_data(char *in, bdbhpt_parsed_data_t *pd) { + + char *endp, *ttlStr; + char *tmp = in; + char *lastchar = (char *) &tmp[strlen(tmp)]; + + /*% + * String should be formated as: + * replication_id + * (a space) + * host_name + * (a space) + * ttl + * (a space) + * type + * (a space) + * remaining data + * + * examples: + * + * 9191 host 10 A 127.0.0.1 + * server1_212 host 10 A 127.0.0.2 + * {xxxx-xxxx-xxxx-xxxx-xxxx} host 10 MX 20 mail.example.com + */ + + /* + * we don't need the replication id, so don't + * bother saving a pointer to it. + */ + + /* find space after replication id */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to host */ + pd->host = tmp; + + /* find space after host and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to ttl string */ + ttlStr = tmp; + + /* find space after ttl and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to dns type */ + pd->type = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) + return ISC_R_FAILURE; + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) + return ISC_R_FAILURE; + + /* save pointer to remainder of DNS data */ + pd->data = tmp; + + /* convert ttl string to integer */ + pd->ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || pd->ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt driver ttl must be a postive number"); + return ISC_R_FAILURE; + } + + /* if we get this far everything should have worked. */ + return ISC_R_SUCCESS; +} + +/* + * DLZ methods + */ + +static isc_result_t +bdbhpt_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBT key, data; + + /* check to see if we are authoritative for the zone first. */ + result = bdbhpt_findzone(driverarg, dbdata, name); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + memset(&key, 0, sizeof(DBT)); + key.flags = DB_DBT_MALLOC; + key.data = strdup(name); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + key.size = strlen(key.data); + + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + data.data = strdup(client); + if (data.data == NULL) { + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + data.size = strlen(data.data); + + switch(db->client->get(db->client, NULL, &key, &data, DB_GET_BOTH)) { + case DB_NOTFOUND: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + + xfr_cleanup: + + /* free any memory duplicate string in the key field */ + if (key.data != NULL) + free(key.data); + + /* free any memory allocated to the data field. */ + if (data.data != NULL) + free(data.data); + + return result; + +} + +/*% + * BDB does not allow a secondary index on a database that allows + * duplicates. We have a few options: + * + * 1) kill speed by having lookup method use a secondary db which + * is associated to the primary DB with the DNS data. Then have + * another secondary db for zone transfer which also points to + * the dns_data primary. NO - The point of this driver is + * lookup performance. + * + * 2) Blow up database size by storing DNS data twice. Once for + * the lookup (dns_data) database, and a second time for the zone + * transfer (dns_xfr) database. NO - That would probably require + * a larger cache to provide good performance. Also, that would + * make the DB larger on disk potentially slowing it as well. + * + * 3) Loop through the dns_xfr database with a cursor to get + * all the different hosts in a zone. Then use the zone & host + * together to lookup the data in the dns_data database. YES - + * This may slow down zone xfr's a little, but that's ok they + * don't happen as often and don't need to be as fast. We can + * also use this table when deleting a zone (The BDB driver + * is read only - the delete would be used during replication + * updates by a separate process). + */ + +static isc_result_t +bdbhpt_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBC *xfr_cursor = NULL; + DBC *dns_cursor = NULL; + DBT xfr_key, xfr_data, dns_key, dns_data; + int xfr_flags; + int dns_flags; + int bdbhptres; + bdbhpt_parsed_data_t pd; + char *tmp = NULL, *tmp_zone, *tmp_zone_host = NULL; + + UNUSED(driverarg); + + memset(&xfr_key, 0, sizeof(DBT)); + memset(&xfr_data, 0, sizeof(DBT)); + memset(&dns_key, 0, sizeof(DBT)); + memset(&dns_data, 0, sizeof(DBT)); + + xfr_key.data = tmp_zone = strdup(zone); + if (xfr_key.data == NULL) + return (ISC_R_NOMEMORY); + + xfr_key.size = strlen(xfr_key.data); + + /* get a cursor to loop through dns_xfr table */ + if (db->xfr->cursor(db->xfr, NULL, &xfr_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + /* get a cursor to loop through dns_data table */ + if (db->data->cursor(db->data, NULL, &dns_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + xfr_flags = DB_SET; + + /* loop through xfr table for specified zone. */ + while ((bdbhptres = xfr_cursor->c_get(xfr_cursor, &xfr_key, &xfr_data, + xfr_flags)) == 0) { + + xfr_flags = DB_NEXT_DUP; + + /* +1 to allow for space between zone and host names */ + dns_key.size = xfr_data.size + xfr_key.size + 1; + + /* +1 to allow for null term at end of string. */ + dns_key.data = tmp_zone_host = malloc(dns_key.size + 1); + if (dns_key.data == NULL) + goto allnodes_cleanup; + + /* + * construct search key for dns_data. + * zone_name(a space)host_name + */ + strcpy(dns_key.data, zone); + strcat(dns_key.data, " "); + strncat(dns_key.data, xfr_data.data, xfr_data.size); + + dns_flags = DB_SET; + + while ((bdbhptres = dns_cursor->c_get(dns_cursor, &dns_key, + &dns_data, + dns_flags)) == 0) { + + dns_flags = DB_NEXT_DUP; + + /* +1 to allow for null term at end of string. */ + tmp = realloc(tmp, dns_data.size + 1); + if (tmp == NULL) + goto allnodes_cleanup; + + /* copy data to tmp string, and append null term. */ + strncpy(tmp, dns_data.data, dns_data.size); + tmp[dns_data.size] = '\0'; + + /* split string into dns data parts. */ + if (bdbhpt_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto allnodes_cleanup; + + result = dns_sdlz_putnamedrr(allnodes, pd.host, + pd.type, pd.ttl, pd.data); + if (result != ISC_R_SUCCESS) + goto allnodes_cleanup; + + } /* end inner while loop */ + + /* clean up memory */ + if (tmp_zone_host != NULL) { + free(tmp_zone_host); + tmp_zone_host = NULL; + } + } /* end outer while loop */ + + allnodes_cleanup: + + /* free any memory */ + if (tmp != NULL) + free(tmp); + + if (tmp_zone_host != NULL) + free(tmp_zone_host); + + if (tmp_zone != NULL) + free(tmp_zone); + + /* get rid of cursors */ + if (xfr_cursor != NULL) + xfr_cursor->c_close(xfr_cursor); + + if (dns_cursor != NULL) + dns_cursor->c_close(xfr_cursor); + + return result; +} + +/*% + * Performs bdbhpt cleanup. + * Used by bdbhpt_create if there is an error starting up. + * Used by bdbhpt_destroy when the driver is shutting down. + */ + +static void +bdbhpt_cleanup(bdbhpt_instance_t *db) { + + isc_mem_t *mctx; + + /* close databases */ + if (db->data != NULL) + db->data->close(db->data, 0); + if (db->xfr != NULL) + db->xfr->close(db->xfr, 0); + if (db->zone != NULL) + db->zone->close(db->zone, 0); + if (db->client != NULL) + db->client->close(db->client, 0); + + /* close environment */ + if (db->dbenv != NULL) + db->dbenv->close(db->dbenv, 0); + + /* cleanup memory */ + if (db->mctx != NULL) { + /* save mctx for later */ + mctx = db->mctx; + /* return, and detach the memory */ + isc_mem_put(mctx, db, sizeof(bdbhpt_instance_t)); + isc_mem_detach(&mctx); + } +} + +static isc_result_t +bdbhpt_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBT key, data; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + key.data = strdup(name); + + if (key.data == NULL) + return (ISC_R_NOMEMORY); + + /* + * reverse string to take advantage of BDB locality of reference + * if we need futher lookups because the zone doesn't match the + * first time. + */ + key.data = bdbhpt_strrev(key.data); + key.size = strlen(key.data); + + switch(db->zone->get(db->zone, NULL, &key, &data, 0)) { + case DB_NOTFOUND: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + + /* free any memory duplicate string in the key field */ + if (key.data != NULL) + free(key.data); + + /* free any memory allocated to the data field. */ + if (data.data != NULL) + free(data.data); + + return result; +} + +static isc_result_t +bdbhpt_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + + isc_result_t result = ISC_R_NOTFOUND; + bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata; + DBC *data_cursor = NULL; + DBT key, data; + int bdbhptres; + int flags; + + bdbhpt_parsed_data_t pd; + char *tmp = NULL; + char *keyStr = NULL; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.size = strlen(zone) + strlen(name) + 1; + + /* allocate mem for key */ + key.data = keyStr = malloc((key.size + 1) * sizeof(char)); + + if (keyStr == NULL) + return ISC_R_NOMEMORY; + + strcpy(keyStr, zone); + strcat(keyStr, " "); + strcat(keyStr, name); + + /* get a cursor to loop through data */ + if (db->data->cursor(db->data, NULL, &data_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + result = ISC_R_NOTFOUND; + + flags = DB_SET; + while ((bdbhptres = data_cursor->c_get(data_cursor, &key, &data, + flags)) == 0) { + + flags = DB_NEXT_DUP; + tmp = realloc(tmp, data.size + 1); + if (tmp == NULL) + goto lookup_cleanup; + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if (bdbhpt_parse_data(tmp, &pd) != ISC_R_SUCCESS) + goto lookup_cleanup; + + result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data); + + if (result != ISC_R_SUCCESS) + goto lookup_cleanup; + } /* end while loop */ + + lookup_cleanup: + + /* get rid of cursor */ + if (data_cursor != NULL) + data_cursor->c_close(data_cursor); + + if (keyStr != NULL) + free(keyStr); + if (tmp != NULL) + free(tmp); + + return result; +} + +/*% Initializes, sets flags and then opens Berkeley databases. */ + +static isc_result_t +bdbhpt_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags) { + + int result; + + /* Initialize the database. */ + if ((result = db_create(db, db_env, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt could not initialize %s database. " + "bdbhpt error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* set database flags. */ + if ((result = (*db)->set_flags(*db, flags)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt could not set flags for %s database. " + "bdbhpt error: %s", + db_name, db_strerror(result)); + return ISC_R_FAILURE; + } + + /* open the database. */ + if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type, + DB_RDONLY | bdbhpt_threads, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt could not open %s database in %s. " + "bdbhpt error: %s", + db_name, db_file, db_strerror(result)); + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +static isc_result_t +bdbhpt_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + int bdbhptres; + int bdbFlags = 0; + bdbhpt_instance_t *db = NULL; + + UNUSED(dlzname); + UNUSED(driverarg); + + /* verify we have 4 arg's passed to the driver */ + if (argc != 4) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt driver requires at least " + "3 command line args."); + return (ISC_R_FAILURE); + } + + switch((char) *argv[1]) { + /* + * Transactional mode. Highest safety - lowest speed. + */ + case 'T': + case 't': + bdbFlags = DB_INIT_MPOOL | DB_INIT_LOCK | + DB_INIT_LOG | DB_INIT_TXN; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "bdbhpt driver using transactional mode."); + break; + /* + * Concurrent mode. Lower safety (no rollback) - + * higher speed. + */ + case 'C': + case 'c': + bdbFlags = DB_INIT_CDB | DB_INIT_MPOOL; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "bdbhpt driver using concurrent mode."); + break; + /* + * Private mode. No inter-process communication & no locking. + * Lowest saftey - highest speed. + */ + case 'P': + case 'p': + bdbFlags = DB_PRIVATE | DB_INIT_MPOOL; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "bdbhpt driver using private mode."); + break; + default: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt driver requires the operating mode " + "be set to P or C or T. You specified '%s'", + argv[1]); + return (ISC_R_FAILURE); + } + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(ns_g_mctx, sizeof(bdbhpt_instance_t)); + if (db == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for " + "database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(bdbhpt_instance_t)); + + /* attach to the memory context */ + isc_mem_attach(ns_g_mctx, &db->mctx); + + /* + * create bdbhpt environment + * Basically bdbhpt allocates and assigns memory to db->dbenv + */ + bdbhptres = db_env_create(&db->dbenv, 0); + if (bdbhptres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt environment could not be created. " + "bdbhpt error: %s", + db_strerror(bdbhptres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open bdbhpt environment */ + bdbhptres = db->dbenv->open(db->dbenv, argv[2], + bdbFlags | bdbhpt_threads | DB_CREATE, 0); + if (bdbhptres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "bdbhpt environment at '%s' could not be opened." + " bdbhpt error: %s", + argv[2], db_strerror(bdbhptres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open dlz_data database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->data, + dlz_data, argv[3], DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_xfr database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->xfr, + dlz_xfr, argv[3], DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_zone database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->zone, + dlz_zone, argv[3], 0); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + /* open dlz_client database. */ + result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->client, + dlz_client, argv[3], DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) + goto init_cleanup; + + *dbdata = db; + + return(ISC_R_SUCCESS); + + init_cleanup: + + bdbhpt_cleanup(db); + return result; +} + +static void +bdbhpt_destroy(void *driverarg, void *dbdata) +{ + UNUSED(driverarg); + + bdbhpt_cleanup((bdbhpt_instance_t *) dbdata); +} + +/* + * bdbhpt_authority not needed as authority data is returned by lookup + */ +static dns_sdlzmethods_t dlz_bdbhpt_methods = { + bdbhpt_create, + bdbhpt_destroy, + bdbhpt_findzone, + bdbhpt_lookup, + NULL, + bdbhpt_allnodes, + bdbhpt_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_bdbhpt_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ bdbhpt driver."); + + result = dns_sdlzregister("bdbhpt", &dlz_bdbhpt_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + ns_g_mctx, &dlz_bdbhpt); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_bdbhpt_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ bdbhpt driver."); + + if (dlz_bdbhpt != NULL) + dns_sdlzunregister(&dlz_bdbhpt); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_drivers.c b/contrib/dlz/drivers/dlz_drivers.c new file mode 100644 index 00000000000..e2c6a6e10dc --- /dev/null +++ b/contrib/dlz/drivers/dlz_drivers.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC") + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: dlz_drivers.c,v 1.2 2005/09/05 00:10:55 marka Exp $ */ + +/*! \file */ + +#include + +#include + +/* + * Pull in declarations for this module's functions. + */ + +#include + +/* + * Pull in driver-specific stuff. + */ + +#ifdef DLZ_STUB +#include +#endif + +#ifdef DLZ_POSTGRES +#include +#endif + +#ifdef DLZ_MYSQL +#include +#endif + +#ifdef DLZ_FILESYSTEM +#include +#endif + +#ifdef DLZ_BDB +#include +#include +#endif + +#ifdef DLZ_LDAP +#include +#endif + +#ifdef DLZ_ODBC +#include +#endif + +/*% + * Call init functions for all relevant DLZ drivers. + */ + +isc_result_t +dlz_drivers_init(void) { + + isc_result_t result = ISC_R_SUCCESS; + +#ifdef DLZ_STUB + result = dlz_stub_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + +#ifdef DLZ_POSTGRES + result = dlz_postgres_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + +#ifdef DLZ_MYSQL + result = dlz_mysql_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + +#ifdef DLZ_FILESYSTEM + result = dlz_fs_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + +#ifdef DLZ_BDB + result = dlz_bdb_init(); + if (result != ISC_R_SUCCESS) + return (result); + result = dlz_bdbhpt_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + +#ifdef DLZ_LDAP + result = dlz_ldap_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + +#ifdef DLZ_ODBC + result = dlz_odbc_init(); + if (result != ISC_R_SUCCESS) + return (result); +#endif + + return (result); +} + +/*% + * Call shutdown functions for all relevant DLZ drivers. + */ + +void +dlz_drivers_clear(void) { + +#ifdef DLZ_STUB + dlz_stub_clear(); +#endif + +#ifdef DLZ_POSTGRES + dlz_postgres_clear(); +#endif + +#ifdef DLZ_MYSQL + dlz_mysql_clear(); +#endif + +#ifdef DLZ_FILESYSTEM + dlz_fs_clear(); +#endif + +#ifdef DLZ_BDB + dlz_bdb_clear(); + dlz_bdbhpt_clear(); +#endif + +#ifdef DLZ_LDAP + dlz_ldap_clear(); +#endif + +#ifdef DLZ_ODBC + dlz_odbc_clear(); +#endif + +} diff --git a/contrib/dlz/drivers/dlz_filesystem_driver.c b/contrib/dlz/drivers/dlz_filesystem_driver.c new file mode 100644 index 00000000000..d41d5ac96c0 --- /dev/null +++ b/contrib/dlz/drivers/dlz_filesystem_driver.c @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_FILESYSTEM + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static dns_sdlzimplementation_t *dlz_fs = NULL; + +typedef struct config_data { + char *basedir; + int basedirsize; + char *datadir; + int datadirsize; + char *xfrdir; + int xfrdirsize; + int splitcnt; + char separator; + char pathsep; + isc_mem_t *mctx; +} config_data_t; + +typedef struct dir_entry dir_entry_t; + +struct dir_entry { + char dirpath[ISC_DIR_PATHMAX]; + ISC_LINK(dir_entry_t) link; +}; + +typedef ISC_LIST(dir_entry_t) dlist_t; + +/* forward reference */ + +static void +fs_destroy(void *driverarg, void *dbdata); + +/* + * Private methods + */ + +static isc_boolean_t +is_safe(const char *input) +{ + unsigned int i; + unsigned int len = strlen(input); + + /* check that only allowed characters are in the domain name */ + for (i=0; i < len; i++) { + /* '.' is allowed, but has special requirements */ + if (input[i] == '.') { + /* '.' is not allowed as first char */ + if (i == 0) + return ISC_FALSE; + /* '..', two dots together is not allowed. */ + else if (input[i-1] == '.') + return ISC_FALSE; + /* '.' is not allowed as last char */ + if (i == len) + return ISC_FALSE; + /* only 1 dot in ok location, continue at next char */ + continue; + } + /* '-' is allowed, continue at next char */ + if (input[i] == '-') + continue; + /* 0-9 is allowed, continue at next char */ + if (input[i] >= '0' && input[i] <= '9') + continue; + /* A-Z uppercase is allowed, continue at next char */ + if (input[i] >= 'A' && input[i] <= 'Z') + continue; + /* a-z lowercase is allowed, continue at next char */ + if (input[i] >= 'a' && input[i] <= 'z') + continue; + + /* + * colon needs to be allowed for IPV6 client + * addresses. Not dangerous in domain names, as not a + * special char. + */ + if (input[i] == ':') + continue; + + /* + * '@' needs to be allowed for in zone data. Not + * dangerous in domain names, as not a special char. + */ + if (input[i] == '@') + continue; + + /* + * if we reach this point we have encountered a + * disallowed char! + */ + return ISC_FALSE; + } + /* everything ok. */ + return ISC_TRUE; +} + +static isc_result_t +create_path_helper(char *out, const char *in, config_data_t *cd) +{ + + char *tmpString; + char *tmpPtr; + int i; + + tmpString = isc_mem_strdup(ns_g_mctx, in); + if (tmpString == NULL) + return (ISC_R_NOMEMORY); + + /* + * don't forget is_safe guarantees '.' will NOT be the + * first/last char + */ + while ((tmpPtr = strrchr(tmpString, '.')) != NULL) { + i = 0; + while (tmpPtr[i+1] != '\0') { + if (cd->splitcnt < 1) + strcat(out, (char *) &tmpPtr[i+1]); + else + strncat(out, (char *) &tmpPtr[i+1], + cd->splitcnt); + strncat(out, (char *) &cd->pathsep, 1); + if (cd->splitcnt == 0) + break; + if (strlen((char *) &tmpPtr[i+1]) <= + (unsigned int) cd->splitcnt) + break; + i += cd->splitcnt; + } + tmpPtr[0] = '\0'; + } + + /* handle the "first" label properly */ + i=0; + tmpPtr = tmpString; + while (tmpPtr[i] != '\0') { + if (cd->splitcnt < 1) + strcat(out, (char *) &tmpPtr[i]); + else + strncat(out, (char *) &tmpPtr[i], cd->splitcnt); + strncat(out, (char *) &cd->pathsep, 1); + if (cd->splitcnt == 0) + break; + if (strlen((char *) &tmpPtr[i]) <= + (unsigned int) cd->splitcnt) + break; + i += cd->splitcnt; + } + + isc_mem_free(ns_g_mctx, tmpString); + return (ISC_R_SUCCESS); +} + +/*% + * Checks to make sure zone and host are safe. If safe, then + * hashes zone and host strings to build a path. If zone / host + * are not safe an error is returned. + */ + +static isc_result_t +create_path(const char *zone, const char *host, const char *client, + config_data_t *cd, char **path) +{ + + char *tmpPath; + int pathsize; + int len; + isc_result_t result; + + /* we require a zone & cd parameter */ + REQUIRE(zone != NULL); + REQUIRE(cd != NULL); + /* require path to be a pointer to NULL */ + REQUIRE(path != NULL && *path == NULL); + /* + * client and host may both be NULL, but they can't both be + * NON-NULL + */ + REQUIRE( (host == NULL && client == NULL) || + (host != NULL && client == NULL) || + (host == NULL && client != NULL) ); + + /* if the requested zone is "unsafe", return error */ + if (is_safe(zone) != ISC_TRUE) + return (ISC_R_FAILURE); + + /* if host was passed, verify that it is safe */ + if ((host != NULL) && (is_safe(host) != ISC_TRUE) ) + return (ISC_R_FAILURE); + + /* if host was passed, verify that it is safe */ + if ((client != NULL) && (is_safe(client) != ISC_TRUE) ) + return (ISC_R_FAILURE); + + /* Determine how much memory the split up string will require */ + if (host != NULL) + len = strlen(zone) + strlen(host); + else if (client != NULL) + len = strlen(zone) + strlen(client); + else + len = strlen(zone); + + /* + * even though datadir and xfrdir will never be in the same + * string we only waste a few bytes by allocating for both, + * and then we are safe from buffer overruns. + */ + pathsize = len + cd->basedirsize + + cd->datadirsize + cd->xfrdirsize + 4; + + /* if we are splitting names, we will need extra space. */ + if (cd->splitcnt > 0) + pathsize += len/cd->splitcnt; + + tmpPath = isc_mem_allocate(ns_g_mctx , pathsize * sizeof(char)); + if (tmpPath == NULL) { + /* write error message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver unable to " + "allocate memory in create_path()."); + result = ISC_R_NOMEMORY; + goto cleanup_mem; + } + + /* + * build path string. + * start out with base directory. + */ + strcpy(tmpPath, cd->basedir); + + /* add zone name - parsed properly */ + if ((result = create_path_helper(tmpPath, zone, cd)) != ISC_R_SUCCESS) + goto cleanup_mem; + + /* + * When neither client or host is passed we are building a + * path to see if a zone is supported. We require that a zone + * path have the "data dir" directory contained within it so + * that we know this zone is really supported. Otherwise, + * this zone may not really be supported because we are + * supporting a delagated sub zone. + * + * Example: + * + * We are supporting long.domain.com and using a splitcnt of + * 0. the base dir is "/base-dir/" and the data dir is + * "/.datadir" We want to see if we are authoritative for + * domain.com. Path /base-dir/com/domain/.datadir since + * /base-dir/com/domain/.datadir does not exist, we are not + * authoritative for the domain "domain.com". However we are + * authoritative for the domain "long.domain.com" because the + * path /base-dir/com/domain/long/.datadir does exist! + */ + + /* if client is passed append xfr dir, otherwise append data dir */ + if (client != NULL) { + strcat(tmpPath, cd->xfrdir); + strncat(tmpPath, (char *) &cd->pathsep, 1); + strcat(tmpPath, client); + } else { + strcat(tmpPath, cd->datadir); + } + + /* if host not null, add it. */ + if (host != NULL) { + strncat(tmpPath, (char *) &cd->pathsep, 1); + if ((result = create_path_helper(tmpPath, host, + cd)) != ISC_R_SUCCESS) + goto cleanup_mem; + } + + /* return the path we built. */ + *path = tmpPath; + + /* return success */ + result = ISC_R_SUCCESS; + + cleanup_mem: + /* cleanup memory */ + + /* free tmpPath memory */ + if (tmpPath != NULL && result != ISC_R_SUCCESS) + isc_mem_free(ns_g_mctx, tmpPath); + + /* free tmpPath memory */ + return result; +} + +static isc_result_t +process_dir(isc_dir_t dir, void *passback, config_data_t *cd, + dlist_t *dir_list, unsigned int basedirlen) +{ + + char tmp[ISC_DIR_PATHMAX + ISC_DIR_NAMEMAX]; + int astPos; + struct stat sb; + isc_result_t result = ISC_R_FAILURE; + char *endp; + char *type; + char *ttlStr; + char *data; + char host[ISC_DIR_NAMEMAX]; + char *tmpString; + char *tmpPtr; + int ttl; + int i; + int len; + dir_entry_t *direntry; + isc_boolean_t foundHost; + + tmp[0] = '\0'; /* set 1st byte to '\0' so strcpy works right. */ + host[0] = '\0'; + foundHost = ISC_FALSE; + + /* copy base directory name to tmp. */ + strcpy(tmp, dir.dirname); + + /* dir.dirname will always have '*' as the last char. */ + astPos = strlen(dir.dirname) - 1; + + /* if dir_list != NULL, were are performing a zone xfr */ + if (dir_list != NULL) { + /* if splitcnt == 0, determine host from path. */ + if (cd->splitcnt == 0) { + if (strlen(tmp) - 3 > basedirlen) { + tmp[astPos-1] = '\0'; + tmpString = (char *) &tmp[basedirlen+1]; + /* handle filesystem's special wildcard "-" */ + if (strcmp(tmpString, "-") == 0) { + strcpy(host, "*"); + } else { + /* + * not special wildcard -- normal name + */ + while ((tmpPtr = strrchr(tmpString, + cd->pathsep)) + != NULL) { + strcat(host, tmpPtr + 1); + strcat(host, "."); + tmpPtr[0] = '\0'; + } + strcat(host, tmpString); + } + + foundHost = ISC_TRUE; + /* set tmp again for use later */ + strcpy(tmp, dir.dirname); + } + } else { + /* + * if splitcnt != 0 determine host from + * ".host" directory entry + */ + while (isc_dir_read(&dir) == ISC_R_SUCCESS) { + if (strncasecmp(".host", + dir.entry.name, 5) == 0) { + /* + * handle filesystem's special + * wildcard "-" + */ + if (strcmp((char *) &dir.entry.name[6], + "-") == 0) + strcpy(host, "*"); + else + strcpy(host, + (char *) + &dir.entry.name[6]); + foundHost = ISC_TRUE; + break; + } + } + /* reset dir list for use later */ + isc_dir_reset(&dir); + } /* end of else */ + } + + while (isc_dir_read(&dir) == ISC_R_SUCCESS) { + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Filesystem driver Dir name:" + " '%s' Dir entry: '%s'\n", + dir.dirname, dir.entry.name); + + /* skip any entries starting with "." */ + if (dir.entry.name[0] == '.') + continue; + + /* + * get rid of '*', set to NULL. Effectively trims + * string from previous loop to base directory only + * while still leaving memory for concat to be + * performed next. + */ + + tmp[astPos] = '\0'; + + /* add name to base directory name. */ + strcat(tmp, dir.entry.name); + + /* make sure we can stat entry */ + if (stat(tmp, &sb) == 0 ) { + /* if entry is a directory */ + if ((sb.st_mode & S_IFDIR) != 0) { + /* + * if dir list is NOT NULL, add dir to + * dir list + */ + if (dir_list != NULL) { + direntry = + isc_mem_get(ns_g_mctx, + sizeof(dir_entry_t)); + if (direntry == NULL) + return (ISC_R_NOMEMORY); + strcpy(direntry->dirpath, tmp); + ISC_LINK_INIT(direntry, link); + ISC_LIST_APPEND(*dir_list, direntry, + link); + result = ISC_R_SUCCESS; + } + continue; + + /* + * if entry is a file be sure we do + * not add entry to DNS results if we + * are performing a zone xfr and we + * could not find a host entry. + */ + + } else if (dir_list != NULL && + foundHost == ISC_FALSE) { + continue; + } + } else /* if we cannot stat entry, skip it. */ + continue; + + type = dir.entry.name; + ttlStr = strchr(type, cd->separator); + if (ttlStr == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver: " + "%s could not be parsed properly", + tmp); + return ISC_R_FAILURE; + } + + /* replace separator char with NULL to split string */ + ttlStr[0] = '\0'; + /* start string after NULL of previous string */ + ttlStr = (char *) &ttlStr[1]; + + data = strchr(ttlStr, cd->separator); + if (data == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver: " + "%s could not be parsed properly", + tmp); + return ISC_R_FAILURE; + } + + /* replace separator char with NULL to split string */ + data[0] = '\0'; + + /* start string after NULL of previous string */ + data = (char *) &data[1]; + + /* replace all cd->separator chars with a space. */ + len = strlen(data); + + for (i=0; i < len; i++) { + if (data[i] == cd->separator) + data[i] = ' '; + } + + /* convert text to int, make sure it worked right */ + ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver " + "ttl must be a postive number"); + } + + /* pass data back to Bind */ + if (dir_list == NULL) + result = dns_sdlz_putrr((dns_sdlzlookup_t *) passback, + type, ttl, data); + else + result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *) + passback, + (char *) host, + type, ttl, data); + + /* if error, return error right away */ + if (result != ISC_R_SUCCESS) + return result; + } /* end of while loop */ + + return result; +} + +/* + * SDLZ interface methods + */ + +static isc_result_t +fs_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + + isc_result_t result; + char *path; + struct stat sb; + config_data_t *cd; + path = NULL; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + if (create_path(name, NULL, client, cd, &path) != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_AXFR; + } + + if ((sb.st_mode & S_IFREG) != 0) { + result = ISC_R_SUCCESS; + goto complete_AXFR; + } + + result = ISC_R_NOTFOUND; + + complete_AXFR: + isc_mem_free(ns_g_mctx, path); + return result; +} + +static isc_result_t +fs_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result; + dlist_t *dir_list; + config_data_t *cd; + char *basepath; + unsigned int basepathlen; + struct stat sb; + isc_dir_t dir; + dir_entry_t *dir_entry; + dir_entry_t *next_de; + + basepath = NULL; + dir_list = NULL; + + UNUSED(driverarg); + UNUSED(allnodes); + + cd = (config_data_t *) dbdata; + + /* allocate memory for list */ + dir_list = isc_mem_get(ns_g_mctx, sizeof(dlist_t)); + if (dir_list == NULL) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + /* initialize list */ + ISC_LIST_INIT(*dir_list); + + if (create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + /* remove path separator at end of path so stat works properly */ + basepathlen = strlen(basepath); + + if (stat(basepath, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + if ((sb.st_mode & S_IFDIR) == 0) { + result = ISC_R_NOTFOUND; + goto complete_allnds; + } + + /* initialize and open directory */ + isc_dir_init(&dir); + result = isc_dir_open(&dir, basepath); + + /* if directory open failed, return error. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", + basepath); + result = ISC_R_FAILURE; + goto complete_allnds; + } + + /* process the directory */ + result = process_dir(dir, allnodes, cd, dir_list, basepathlen); + + /* close the directory */ + isc_dir_close(&dir); + + if (result != ISC_R_SUCCESS) + goto complete_allnds; + + /* get first dir entry from list. */ + dir_entry = ISC_LIST_HEAD(*dir_list); + while (dir_entry != NULL) { + + result = isc_dir_open(&dir, dir_entry->dirpath); + /* if directory open failed, return error. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Unable to open %s " + "directory to read entries.", + basepath); + result = ISC_R_FAILURE; + goto complete_allnds; + } + + /* process the directory */ + result = process_dir(dir, allnodes, cd, dir_list, basepathlen); + + /* close the directory */ + isc_dir_close(&dir); + + if (result != ISC_R_SUCCESS) + goto complete_allnds; + + dir_entry = ISC_LIST_NEXT(dir_entry, link); + } /* end while */ + + complete_allnds: + if (dir_list != NULL) { + /* clean up entries from list. */ + dir_entry = ISC_LIST_HEAD(*dir_list); + while (dir_entry != NULL) { + next_de = ISC_LIST_NEXT(dir_entry, link); + isc_mem_put(ns_g_mctx, dir_entry, sizeof(dir_entry_t)); + dir_entry = next_de; + } /* end while */ + isc_mem_put(ns_g_mctx, dir_list, sizeof(dlist_t)); + } + + if (basepath != NULL) + isc_mem_free(ns_g_mctx, basepath); + + return result; +} + +static isc_result_t +fs_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + char *path; + struct stat sb; + path = NULL; + + UNUSED(driverarg); + + if (create_path(name, NULL, NULL, (config_data_t *) dbdata, + &path) != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Filesystem driver Findzone() Checking for path: '%s'\n", + path); + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_FZ; + } + + if ((sb.st_mode & S_IFDIR) != 0) { + result = ISC_R_SUCCESS; + goto complete_FZ; + } + + result = ISC_R_NOTFOUND; + + complete_FZ: + + isc_mem_free(ns_g_mctx, path); + return result; +} + +static isc_result_t +fs_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + char *path; + struct stat sb; + isc_dir_t dir; + path = NULL; + + UNUSED(driverarg); + UNUSED(lookup); + + if (strcmp(name, "*") == 0) + /* + * handle filesystem's special wildcard "-" + */ + result = create_path(zone, "-", NULL, + (config_data_t *) dbdata, &path); + else + result = create_path(zone, name, NULL, + (config_data_t *) dbdata, &path); + + if ( result != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + /* remove path separator at end of path so stat works properly */ + path[strlen(path)-1] = '\0'; + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Filesystem driver lookup() Checking for path: '%s'\n", + path); + + + if (stat(path, &sb) != 0) { + result = ISC_R_NOTFOUND; + goto complete_lkup; + } + + if ((sb.st_mode & S_IFDIR) == 0) { + result = ISC_R_NOTFOUND; + goto complete_lkup; + } + + /* initialize and open directory */ + isc_dir_init(&dir); + result = isc_dir_open(&dir, path); + + /* if directory open failed, return error. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Unable to open %s directory to read entries.", + path); + result = ISC_R_FAILURE; + goto complete_lkup; + } + + /* process any records in the directory */ + result = process_dir(dir, lookup, (config_data_t *) dbdata, NULL, 0); + + /* close the directory */ + isc_dir_close(&dir); + + complete_lkup: + + isc_mem_free(ns_g_mctx, path); + return result; +} + +static isc_result_t +fs_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + config_data_t *cd; + char *endp; + int len; + char pathsep; + + UNUSED(driverarg); + UNUSED(dlzname); + + /* we require 5 command line args. */ + if (argc != 6) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver requires " + "6 command line args."); + return (ISC_R_FAILURE); + } + + if (strlen(argv[5]) > 1) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver can only " + "accept a single character for separator."); + return (ISC_R_FAILURE); + } + + /* verify base dir ends with '/' or '\' */ + len = strlen(argv[1]); + if (argv[1][len-1] != '\\' && argv[1][len-1] != '/') { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Base dir parameter for filesystem driver " + "should end with %s", + "either '/' or '\\' "); + return (ISC_R_FAILURE); + } + + /* determine and save path separator for later */ + if (argv[1][len-1] == '\\') + pathsep = '\\'; + else + pathsep = '/'; + + /* allocate memory for our config data */ + cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t)); + if (cd == NULL) + goto no_mem; + + /* zero the memory */ + memset(cd, 0, sizeof(config_data_t)); + + cd->pathsep = pathsep; + + /* get and store our base directory */ + cd->basedir = isc_mem_strdup(ns_g_mctx, argv[1]); + if (cd->basedir == NULL) + goto no_mem; + cd->basedirsize = strlen(cd->basedir); + + /* get and store our data sub-dir */ + cd->datadir = isc_mem_strdup(ns_g_mctx, argv[2]); + if (cd->datadir == NULL) + goto no_mem; + cd->datadirsize = strlen(cd->datadir); + + /* get and store our zone xfr sub-dir */ + cd->xfrdir = isc_mem_strdup(ns_g_mctx, argv[3]); + if (cd->xfrdir == NULL) + goto no_mem; + cd->xfrdirsize = strlen(cd->xfrdir); + + /* get and store our directory split count */ + cd->splitcnt = strtol(argv[4], &endp, 10); + if (*endp != '\0' || cd->splitcnt < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Directory split count must be zero (0) " + "or a postive number"); + } + + /* get and store our separator character */ + cd->separator = *argv[5]; + + /* attach config data to memory context */ + isc_mem_attach(ns_g_mctx, &cd->mctx); + + /* pass back config data */ + *dbdata = cd; + + /* return success */ + return(ISC_R_SUCCESS); + + /* handle no memory error */ + no_mem: + + /* if we allocated a config data object clean it up */ + if (cd != NULL) + fs_destroy(NULL, cd); + + /* write error message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Filesystem driver unable to " + "allocate memory for config data."); + + /* return error */ + return (ISC_R_NOMEMORY); +} + +static void +fs_destroy(void *driverarg, void *dbdata) +{ + isc_mem_t *mctx; + config_data_t *cd; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + /* + * free memory for each section of config data that was + * allocated + */ + if (cd->basedir != NULL) + isc_mem_free(ns_g_mctx, cd->basedir); + + if (cd->datadir != NULL) + isc_mem_free(ns_g_mctx, cd->datadir); + + if (cd->xfrdir != NULL) + isc_mem_free(ns_g_mctx, cd->xfrdir); + + /* hold memory context to use later */ + mctx = cd->mctx; + + /* free config data memory */ + isc_mem_put(mctx, cd, sizeof(config_data_t)); + + /* detach memory from context */ + isc_mem_detach(&mctx); +} + +static dns_sdlzmethods_t dlz_fs_methods = { + fs_create, + fs_destroy, + fs_findzone, + fs_lookup, + NULL, + fs_allnodes, + fs_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_fs_init(void) +{ + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ filesystem driver."); + + result = dns_sdlzregister("filesystem", &dlz_fs_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA, + ns_g_mctx, &dlz_fs); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_fs_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ filesystem driver."); + + if (dlz_fs != NULL) + dns_sdlzunregister(&dlz_fs); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_ldap_driver.c b/contrib/dlz/drivers/dlz_ldap_driver.c new file mode 100644 index 00000000000..35d75489b9e --- /dev/null +++ b/contrib/dlz/drivers/dlz_ldap_driver.c @@ -0,0 +1,1339 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_LDAP + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* + * Need older API functions from ldap.h. + */ +#define LDAP_DEPRECATED 1 + +#include + +#define SIMPLE "simple" +#define KRB41 "krb41" +#define KRB42 "krb42" +#define V2 "v2" +#define V3 "v3" + +static dns_sdlzimplementation_t *dlz_ldap = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +/*% + * Structure to hold everthing needed by this "instance" of the LDAP + * driver remember, the driver code is only loaded once, but may have + * many separate instances. + */ + +typedef struct { + +#ifdef ISC_PLATFORM_USETHREADS + db_list_t *db; /*%< handle to a list of DB */ +#else + dbinstance_t *db; /*%< handle to db */ +#endif + int method; /*%< security authentication method */ + char *user; /*%< who is authenticating */ + char *cred; /*%< password for simple authentication method */ + int protocol; /*%< LDAP communication protocol version */ + char *hosts; /*%< LDAP server hosts */ + +} ldap_instance_t; + +/* forward references */ + +static isc_result_t +dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name); + +static void +dlz_ldap_destroy(void *driverarg, void *dbdata); + +/* + * Private methods + */ + +/*% checks that the LDAP URL parameters make sense */ + +static isc_result_t +dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg) { + + isc_result_t result = ISC_R_SUCCESS; + int ldap_result; + LDAPURLDesc *ldap_url = NULL; + + if (!ldap_is_ldap_url(URL)) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query is not a valid LDAP URL", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + ldap_result = ldap_url_parse(URL, &ldap_url); + if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "parsing %s query failed", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must specify at least " + "%d attributes to return", + msg, attrCnt); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_host != NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must not specify a host", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_port != 389) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must not specify a port", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s query must specify a search base", msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%s uses extensions. " + "The driver does not support LDAP extensions.", + msg); + result = ISC_R_FAILURE; + goto cleanup; + } + + cleanup: + + if (ldap_url != NULL) + ldap_free_urldesc(ldap_url); + + return result; +} +/*% Connects / reconnects to LDAP server */ + +static isc_result_t +dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) { + + isc_result_t result; + int ldap_result; + + /* if we have a connection, get ride of it. */ + if (dbc->dbconn != NULL) { + ldap_unbind_s((LDAP *) dbc->dbconn); + dbc->dbconn = NULL; + } + + /* now connect / reconnect. */ + + /* initialize. */ + dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT); + if (dbc->dbconn == NULL) + return ISC_R_NOMEMORY; + + /* set protocol version. */ + ldap_result = ldap_set_option((LDAP *) dbc->dbconn, + LDAP_OPT_PROTOCOL_VERSION, + &(dbi->protocol)); + if (ldap_result != LDAP_SUCCESS) { + result = ISC_R_NOPERM; + goto cleanup; + } + + /* "bind" to server. i.e. send username / pass */ + ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user, + dbi->cred, dbi->method); + if (ldap_result != LDAP_SUCCESS) { + result = ISC_R_FAILURE; + goto cleanup; + } + + return ISC_R_SUCCESS; + + cleanup: + + /* cleanup if failure. */ + if (dbc->dbconn != NULL) { + ldap_unbind_s((LDAP *) dbc->dbconn); + dbc->dbconn = NULL; + } + + return result; +} + +#ifdef ISC_PLATFORM_USETHREADS + + +/*% + * Properly cleans up a list of database instances. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ +static void +ldap_destroy_dblist(db_list_t *dblist) +{ + + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + /* get the first DBI in the list */ + ndbi = ISC_LIST_HEAD(*dblist); + + /* loop through the list */ + while (ndbi != NULL) { + dbi = ndbi; + /* get the next DBI in the list */ + ndbi = ISC_LIST_NEXT(dbi, link); + /* release DB connection */ + if (dbi->dbconn != NULL) + ldap_unbind_s((LDAP *) dbi->dbconn); + /* release all memory that comprised a DBI */ + destroy_sqldbinstance(dbi); + } + /* release memory for the list structure */ + isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t)); +} + +/*% + * Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ + +static dbinstance_t * +ldap_find_avail_conn(db_list_t *dblist) +{ + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + /* get top of list */ + head = dbi = ISC_LIST_HEAD(*dblist); + + /* loop through list */ + while (count < dbc_search_limit) { + /* try to lock on the mutex */ + if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) + return dbi; /* success, return the DBI for use. */ + + /* not successful, keep trying */ + dbi = ISC_LIST_NEXT(dbi, link); + + /* check to see if we have gone to the top of the list. */ + if (dbi == NULL) { + count++; + dbi = head; + } + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP driver unable to find available connection " + "after searching %d times", + count); + return NULL; +} + +#endif /* ISC_PLATFORM_USETHREADS */ + +static isc_result_t +ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs, + void *ptr, isc_boolean_t allnodes) +{ + isc_result_t result = ISC_R_SUCCESS; + int i = 0; + int j; + int len; + char *attribute = NULL; + LDAPMessage *entry; + char *endp = NULL; + char *host = NULL; + char *type = NULL; + char *data = NULL; + char **vals = NULL; + int ttl; + + /* make sure there are at least some attributes to process. */ + REQUIRE(attrs != NULL || attrs[0] != NULL); + + /* get the first entry to process */ + entry = ldap_first_entry(dbc, msg); + if (entry == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP no entries to process."); + return ISC_R_FAILURE; + } + + /* loop through all entries returned */ + while (entry != NULL) { + + /* reset for this loop */ + ttl = 0; + len = 0; + i = 0; + attribute = attrs[i]; + + /* determine how much space we need for data string */ + for (j=0; attrs[j] != NULL; j++) { + /* get the list of values for this attribute. */ + vals = ldap_get_values(dbc, entry, attrs[j]); + /* skip empty attributes. */ + if (vals == NULL || ldap_count_values(vals) < 1) + continue; + /* + * we only use the first value. this driver + * does not support multi-valued attributes. + */ + len = len + strlen(vals[0]) + 1; + /* free vals for next loop */ + ldap_value_free(vals); + } /* end for (j=0; attrs[j] != NULL, j++) loop */ + + /* allocate memory for data string */ + data = isc_mem_allocate(ns_g_mctx, len + 1); + if (data == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver unable to allocate memory " + "while processing results"); + result = ISC_R_FAILURE; + goto cleanup; + } + + /* + * Make sure data is null termed at the beginning so + * we can check if any data was stored to it later. + */ + data[0] = '\0'; + + /* reset j to re-use below */ + j = 0; + + /* loop through the attributes in the order specified. */ + while (attribute != NULL) { + + /* get the list of values for this attribute. */ + vals = ldap_get_values(dbc, entry, attribute); + + /* skip empty attributes. */ + if (vals == NULL || vals[0] == NULL) { + /* increment attibute pointer */ + attribute = attrs[++i]; + /* start loop over */ + continue; + } + + /* + * j initially = 0. Increment j each time we + * set a field that way next loop will set + * next field. + */ + switch(j) { + case 0: + j++; + /* + * convert text to int, make sure it + * worked right + */ + ttl = strtol(vals[0], &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, + ISC_LOG_ERROR, + "LDAP driver ttl must " + "be a postive number"); + goto cleanup; + } + break; + case 1: + j++; + type = isc_mem_strdup(ns_g_mctx, vals[0]); + break; + case 2: + j++; + if (allnodes == isc_boolean_true) { + host = isc_mem_strdup(ns_g_mctx, + vals[0]); + } else { + strcpy(data, vals[0]); + } + break; + case 3: + j++; + if (allnodes == isc_boolean_true) { + strcpy(data, vals[0]); + } else { + strcat(data, " "); + strcat(data, vals[0]); + } + break; + default: + strcat(data, " "); + strcat(data, vals[0]); + break; + } /* end switch(j) */ + + /* free values */ + ldap_value_free(vals); + vals = NULL; + + /* increment attibute pointer */ + attribute = attrs[++i]; + } /* end while (attribute != NULL) */ + + if (type == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver unable " + "to retrieve dns type"); + result = ISC_R_FAILURE; + goto cleanup; + } + if (strlen(data) < 1) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver unable " + "to retrieve dns data"); + result = ISC_R_FAILURE; + goto cleanup; + } + if (allnodes == isc_boolean_true) { + if (strcasecmp(host, "~") == 0) + result = dns_sdlz_putnamedrr( + (dns_sdlzallnodes_t *) ptr, + "*", type, ttl, data); + else + result = dns_sdlz_putnamedrr( + (dns_sdlzallnodes_t *) ptr, + host, type, ttl, data); + } + else + result = dns_sdlz_putrr((dns_sdlzlookup_t *) ptr, + type, ttl, data); + + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver failed " + "while sending data to Bind."); + goto cleanup; + } + + /* free memory for type, data and host for next loop */ + isc_mem_free(ns_g_mctx, type); + isc_mem_free(ns_g_mctx, data); + if (host != NULL) + isc_mem_free(ns_g_mctx, host); + + /* get the next entry to process */ + entry = ldap_next_entry(dbc, entry); + } /* end while (entry != NULL) */ + + cleanup: + + /* de-allocate memory */ + if (vals != NULL) + ldap_value_free(vals); + if (host != NULL) + isc_mem_free(ns_g_mctx, host); + if (type != NULL) + isc_mem_free(ns_g_mctx, type); + if (data != NULL) + isc_mem_free(ns_g_mctx, data); + + return result; +} + +/*% + * This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. + */ +static isc_result_t +ldap_get_results(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, void *ptr) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + LDAPURLDesc *ldap_url = NULL; + int ldap_result = 0; + LDAPMessage *ldap_msg = NULL; + int i; + int entries; + + /* get db instance / connection */ +#ifdef ISC_PLATFORM_USETHREADS + + /* find an available DBI from the list */ + dbi = ldap_find_avail_conn((db_list_t *) + ((ldap_instance_t *)dbdata)->db); + +#else /* ISC_PLATFORM_USETHREADS */ + + /* + * only 1 DBI - no need to lock instance lock either + * only 1 thread in the whole process, no possible contention. + */ + dbi = (dbinstance_t *) ((ldap_instance_t *)dbdata)->db; + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* if DBI is null, can't do anything else */ + if (dbi == NULL) + return ISC_R_FAILURE; + + /* set fields */ + if (zone != NULL) { + dbi->zone = isc_mem_strdup(ns_g_mctx, zone); + if (dbi->zone == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->zone = NULL; + } + if (record != NULL) { + dbi->record = isc_mem_strdup(ns_g_mctx, record); + if (dbi->record == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->record = NULL; + } + if (client != NULL) { + dbi->client = isc_mem_strdup(ns_g_mctx, client); + if (dbi->client == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { + dbi->client = NULL; + } + + /* what type of query are we going to run? */ + switch(query) { + case ALLNODES: + /* + * if the query was not passed in from the config file + * then we can't run it. return not_implemented, so + * it's like the code for that operation was never + * built into the driver.... AHHH flexibility!!! + */ + if (dbi->allnodes_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, + dbi->allnodes_q); + } + break; + case ALLOWXFR: + /* same as comments as ALLNODES */ + if (dbi->allowxfr_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, + dbi->allowxfr_q); + } + break; + case AUTHORITY: + /* same as comments as ALLNODES */ + if (dbi->authority_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, + dbi->authority_q); + } + break; + case FINDZONE: + /* this is required. It's the whole point of DLZ! */ + if (dbi->findzone_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. " + "Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, + dbi->findzone_q); + } + break; + case LOOKUP: + /* this is required. It's also a major point of DLZ! */ + if (dbi->lookup_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. " + "Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + querystring = build_querystring(ns_g_mctx, + dbi->lookup_q); + } + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "ldap_get_results"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ + if (querystring == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* + * output the full query string during debug so we can see + * what lame error the query has. + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + /* break URL down into it's component parts, if error cleanup */ + ldap_result = ldap_url_parse(querystring, &ldap_url); + if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + for (i=0; i < 3; i++) { + + /* + * dbi->dbconn may be null if trying to reconnect on a + * previous query failed. + */ + if (dbi->dbconn == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP driver attempting to re-connect"); + + result = dlz_ldap_connect((ldap_instance_t *) dbdata, + dbi); + if (result != ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + continue; + } + } + + /* perform ldap search syncronously */ + ldap_result = ldap_search_s((LDAP *) dbi->dbconn, + ldap_url->lud_dn, + ldap_url->lud_scope, + ldap_url->lud_filter, + ldap_url->lud_attrs, 0, &ldap_msg); + + /* + * check return code. No such object is ok, just + * didn't find what we wanted + */ + switch(ldap_result) { + case LDAP_NO_SUCH_OBJECT: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "No object found matching " + "query requirements"); + result = ISC_R_NOTFOUND; + goto cleanup; + break; + case LDAP_SUCCESS: /* on success do nothing */ + result = ISC_R_SUCCESS; + i = 3; + break; + case LDAP_SERVER_DOWN: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "LDAP driver attempting to re-connect"); + result = dlz_ldap_connect((ldap_instance_t *) dbdata, + dbi); + if (result != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + break; + default: + /* + * other errors not ok. Log error message and + * get out + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP error: %s", + ldap_err2string(ldap_result)); + result = ISC_R_FAILURE; + goto cleanup; + break; + } /* close switch(ldap_result) */ + } /* end for (int i=0 i < 3; i++) */ + + if (result != ISC_R_SUCCESS) + goto cleanup; + + switch(query) { + case ALLNODES: + result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg, + ldap_url->lud_attrs, + ptr, isc_boolean_true); + break; + case AUTHORITY: + case LOOKUP: + result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg, + ldap_url->lud_attrs, + ptr, isc_boolean_false); + break; + case ALLOWXFR: + entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg); + if (entries == 0) + result = ISC_R_NOPERM; + else if (entries > 0) + result = ISC_R_SUCCESS; + else + result = ISC_R_FAILURE; + break; + case FINDZONE: + entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg); + if (entries == 0) + result = ISC_R_NOTFOUND; + else if (entries > 0) + result = ISC_R_SUCCESS; + else + result = ISC_R_FAILURE; + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "ldap_get_results"); + result = ISC_R_UNEXPECTED; + } + + + cleanup: + /* it's always good to cleanup after yourself */ + + /* if we retrieved results, free them */ + if (ldap_msg != NULL) + ldap_msgfree(ldap_msg); + + if (ldap_url != NULL) + ldap_free_urldesc(ldap_url); + + /* cleanup */ + if (dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + if (dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + if (dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + +#ifdef ISC_PLATFORM_USETHREADS + + /* release the lock so another thread can use this dbi */ + isc_mutex_unlock(&dbi->instance_lock); + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* release query string */ + if (querystring != NULL) + isc_mem_free(ns_g_mctx, querystring ); + + /* return result */ + return result; +} + +/* + * DLZ methods + */ + +static isc_result_t +dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + + UNUSED(driverarg); + + /* check to see if we are authoritative for the zone first */ + result = dlz_ldap_findzone(driverarg, dbdata, name); + if (result != ISC_R_SUCCESS) { + return result; + } + + /* get all the zone data */ + return ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL); +} + +static isc_result_t +dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + UNUSED(driverarg); + return ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes); +} + +static isc_result_t +dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + UNUSED(driverarg); + return ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup); +} + +static isc_result_t +dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name) +{ + UNUSED(driverarg); + return ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL); +} + +static isc_result_t +dlz_ldap_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + UNUSED(driverarg); + if (strcmp(name, "*") == 0) + return ldap_get_results(zone, "~", NULL, + LOOKUP, dbdata, lookup); + else + return ldap_get_results(zone, name, NULL, + LOOKUP, dbdata, lookup); +} + + +static isc_result_t +dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + + isc_result_t result; + ldap_instance_t *ldap_inst = NULL; + dbinstance_t *dbi = NULL; + int protocol; + int method; + +#ifdef ISC_PLATFORM_USETHREADS + /* if multi-threaded, we need a few extra variables. */ + int dbcount; + char *endp; +/* db_list_t *dblist = NULL; */ + int i; + +#endif /* ISC_PLATFORM_USETHREADS */ + + UNUSED(dlzname); + UNUSED(driverarg); + +#ifdef ISC_PLATFORM_USETHREADS + /* if debugging, let user know we are multithreaded. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "LDAP driver running multithreaded"); +#else /* ISC_PLATFORM_USETHREADS */ + /* if debugging, let user know we are single threaded. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "LDAP driver running single threaded"); +#endif /* ISC_PLATFORM_USETHREADS */ + + if (argc < 9) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver requires at least " + "8 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 13 arg's should be passed to the driver */ + if (argc > 12) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver cannot accept more than " + "11 command line args."); + return (ISC_R_FAILURE); + } + + /* determine protocol version. */ + if (strncasecmp(argv[2], V2, strlen(V2)) == 0) { + protocol = 2; + } else if (strncasecmp(argv[2], V3, strlen(V3)) == 0) { + protocol = 3; + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver protocol must be either %s or %s", + V2, V3); + return (ISC_R_FAILURE); + } + + /* determine connection method. */ + if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0) { + method = LDAP_AUTH_SIMPLE; + } else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0) { + method = LDAP_AUTH_KRBV41; + } else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0) { + method = LDAP_AUTH_KRBV42; + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver authentication method must be " + "one of %s, %s or %s", + SIMPLE, KRB41, KRB42); + return (ISC_R_FAILURE); + } + + /* multithreaded build can have multiple DB connections */ +#ifdef ISC_PLATFORM_USETHREADS + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver database connection count " + "must be positive."); + return (ISC_R_FAILURE); + } +#endif + + /* check that LDAP URL parameters make sense */ + switch(argc) { + case 12: + result = dlz_ldap_checkURL(argv[11], 0, "allow zone transfer"); + if (result != ISC_R_SUCCESS) + return result; + case 11: + result = dlz_ldap_checkURL(argv[10], 3, "all nodes"); + if (result != ISC_R_SUCCESS) + return result; + case 10: + if (strlen(argv[9]) > 0) { + result = dlz_ldap_checkURL(argv[9], 3, "authority"); + if (result != ISC_R_SUCCESS) + return result; + } + case 9: + result = dlz_ldap_checkURL(argv[8], 3, "lookup"); + if (result != ISC_R_SUCCESS) + return result; + result = dlz_ldap_checkURL(argv[7], 0, "find zone"); + if (result != ISC_R_SUCCESS) + return result; + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + /* allocate memory for LDAP instance */ + ldap_inst = isc_mem_get(ns_g_mctx, sizeof(ldap_instance_t)); + if (ldap_inst == NULL) + return (ISC_R_NOMEMORY); + memset(ldap_inst, 0, sizeof(ldap_instance_t)); + + /* store info needed to automatically re-connect. */ + ldap_inst->protocol = protocol; + ldap_inst->method = method; + ldap_inst->hosts = isc_mem_strdup(ns_g_mctx, argv[6]); + if (ldap_inst->hosts == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + ldap_inst->user = isc_mem_strdup(ns_g_mctx, argv[4]); + if (ldap_inst->user == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + ldap_inst->cred = isc_mem_strdup(ns_g_mctx, argv[5]); + if (ldap_inst->cred == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + /* allocate memory for database connection list */ + ldap_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); + if (ldap_inst->db == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* initialize DB connection list */ + ISC_LIST_INIT(*(ldap_inst->db)); + + /* + * create the appropriate number of database instances (DBI) + * append each new DBI to the end of the list + */ + for (i = 0; i < dbcount; i++) { + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* how many queries were passed in from config file? */ + switch(argc) { + case 9: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, + NULL, argv[7], argv[8], + NULL, &dbi); + break; + case 10: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, + argv[9], argv[7], argv[8], + NULL, &dbi); + break; + case 11: + result = build_sqldbinstance(ns_g_mctx, argv[10], NULL, + argv[9], argv[7], argv[8], + NULL, &dbi); + break; + case 12: + result = build_sqldbinstance(ns_g_mctx, argv[10], + argv[11], argv[9], + argv[7], argv[8], + NULL, &dbi); + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + if (result == ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "LDAP driver created " + "database instance object."); + } else { /* unsuccessful?, log err msg and cleanup. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not create " + "database instance object."); + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + /* when multithreaded, build a list of DBI's */ + ISC_LINK_INIT(dbi, link); + ISC_LIST_APPEND(*(ldap_inst->db), dbi, link); +#else + /* + * when single threaded, hold onto the one connection + * instance. + */ + ldap_inst->db = dbi; + +#endif + /* attempt to connect */ + result = dlz_ldap_connect(ldap_inst, dbi); + + /* + * if db connection cannot be created, log err msg and + * cleanup. + */ + switch(result) { + /* success, do nothing */ + case ISC_R_SUCCESS: + break; + /* + * no memory means ldap_init could not + * allocate memory + */ + case ISC_R_NOMEMORY: +#ifdef ISC_PLATFORM_USETHREADS + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not allocate memory " + "for connection number %u", + i+1); +#else + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not allocate memory " + "for connection"); +#endif + goto cleanup; + break; + /* + * no perm means ldap_set_option could not set + * protocol version + */ + case ISC_R_NOPERM: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not " + "set protocol version."); + result = ISC_R_FAILURE; + goto cleanup; + break; + /* failure means couldn't connect to ldap server */ + case ISC_R_FAILURE: +#ifdef ISC_PLATFORM_USETHREADS + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not " + "bind connection number %u to server.", + i+1); +#else + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "LDAP driver could not " + "bind connection to server."); +#endif + goto cleanup; + break; + /* + * default should never happen. If it does, + * major errors. + */ + default: + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dlz_ldap_create() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + goto cleanup; + break; + } /* end switch(result) */ + + +#ifdef ISC_PLATFORM_USETHREADS + + /* set DBI = null for next loop through. */ + dbi = NULL; + } /* end for loop */ + +#endif /* ISC_PLATFORM_USETHREADS */ + + + /* set dbdata to the ldap_instance we created. */ + *dbdata = ldap_inst; + + /* hey, we got through all of that ok, return success. */ + return(ISC_R_SUCCESS); + + cleanup: + + dlz_ldap_destroy(NULL, ldap_inst); + + return(ISC_R_FAILURE); +} + +void +dlz_ldap_destroy(void *driverarg, void *dbdata) +{ + + UNUSED(driverarg); + + if (dbdata != NULL) { + +#ifdef ISC_PLATFORM_USETHREADS + + /* cleanup the list of DBI's */ + ldap_destroy_dblist((db_list_t *) + ((ldap_instance_t *)dbdata)->db); + +#else /* ISC_PLATFORM_USETHREADS */ + + /* release connection */ + if (((ldap_instance_t *)dbdata)->db->dbconn != NULL) + ldap_unbind_s((LDAP *) + ((ldap_instance_t *)dbdata)->db->dbconn); + + /* destroy single DB instance */ + destroy_sqldbinstance(((ldap_instance_t *)dbdata)->db); + +#endif /* ISC_PLATFORM_USETHREADS */ + + if (((ldap_instance_t *)dbdata)->hosts != NULL) + isc_mem_free(ns_g_mctx, + ((ldap_instance_t *)dbdata)->hosts); + + if (((ldap_instance_t *)dbdata)->user != NULL) + isc_mem_free(ns_g_mctx, + ((ldap_instance_t *)dbdata)->user); + + if (((ldap_instance_t *)dbdata)->cred != NULL) + isc_mem_free(ns_g_mctx, + ((ldap_instance_t *)dbdata)->cred); + + isc_mem_put(ns_g_mctx, dbdata, sizeof(ldap_instance_t)); + } +} + +static dns_sdlzmethods_t dlz_ldap_methods = { + dlz_ldap_create, + dlz_ldap_destroy, + dlz_ldap_findzone, + dlz_ldap_lookup, + dlz_ldap_authority, + dlz_ldap_allnodes, + dlz_ldap_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_ldap_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ ldap driver."); + + result = dns_sdlzregister("ldap", &dlz_ldap_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA, + ns_g_mctx, &dlz_ldap); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_ldap_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ ldap driver."); + + if (dlz_ldap != NULL) + dns_sdlzunregister(&dlz_ldap); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_mysql_driver.c b/contrib/dlz/drivers/dlz_mysql_driver.c new file mode 100644 index 00000000000..7a7867df3d5 --- /dev/null +++ b/contrib/dlz/drivers/dlz_mysql_driver.c @@ -0,0 +1,1060 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_MYSQL + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +static dns_sdlzimplementation_t *dlz_mysql = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define COUNTZONE 5 +#define LOOKUP 6 + +#define safeGet(in) in == NULL ? "" : in + +/* + * Private methods + */ + +/*% + * Allocates memory for a new string, and then constructs the new + * string by "escaping" the input string. The new string is + * safe to be used in queries. This is necessary because we cannot + * be sure of what types of strings are passed to us, and we don't + * want special characters in the string causing problems. + */ + +static char * +mysqldrv_escape_string(MYSQL *mysql, const char *instr) { + + char *outstr; + unsigned int len; + + if (instr == NULL) + return NULL; + + len = strlen(instr); + + outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); + if (outstr == NULL) + return NULL; + + mysql_real_escape_string(mysql, outstr, instr, len); + + return outstr; +} + +/*% + * This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds a single database instance. + * The function will construct and run the query, hopefully getting + * a result set. + */ + +static isc_result_t +mysql_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, MYSQL_RES **rs) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + unsigned int i = 0; + unsigned int j = 0; + int qres = 0; + + if (query != COUNTZONE) + REQUIRE(*rs == NULL); + else + REQUIRE(rs == NULL); + + /* get db instance / connection */ + dbi = (dbinstance_t *) dbdata; + + /* if DBI is null, can't do anything else */ + if (dbi == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + /* what type of query are we going to run? */ + switch(query) { + case ALLNODES: + /* + * if the query was not passed in from the config file + * then we can't run it. return not_implemented, so + * it's like the code for that operation was never + * built into the driver.... AHHH flexibility!!! + */ + if (dbi->allnodes_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case ALLOWXFR: + /* same as comments as ALLNODES */ + if (dbi->allowxfr_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case AUTHORITY: + /* same as comments as ALLNODES */ + if (dbi->authority_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case FINDZONE: + /* this is required. It's the whole point of DLZ! */ + if (dbi->findzone_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. " + "Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + case COUNTZONE: + /* same as comments as ALLNODES */ + if (dbi->countzone_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case LOOKUP: + /* this is required. It's also a major point of DLZ! */ + if (dbi->lookup_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. " + "Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "mysql_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + + /* + * was a zone string passed? If so, make it safe for use in + * queries. + */ + if (zone != NULL) { + dbi->zone = mysqldrv_escape_string((MYSQL *) dbi->dbconn, + zone); + if (dbi->zone == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->zone = NULL; + } + + /* + * was a record string passed? If so, make it safe for use in + * queries. + */ + if (record != NULL) { + dbi->record = mysqldrv_escape_string((MYSQL *) dbi->dbconn, + record); + if (dbi->record == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->record = NULL; + } + + /* + * was a client string passed? If so, make it safe for use in + * queries. + */ + if (client != NULL) { + dbi->client = mysqldrv_escape_string((MYSQL *) dbi->dbconn, + client); + if (dbi->client == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->client = NULL; + } + + /* + * what type of query are we going to run? this time we build + * the actual query to run. + */ + switch(query) { + case ALLNODES: + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + break; + case ALLOWXFR: + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + break; + case AUTHORITY: + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + break; + case FINDZONE: + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + break; + case COUNTZONE: + querystring = build_querystring(ns_g_mctx, dbi->countzone_q); + break; + case LOOKUP: + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "mysql_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ + if (querystring == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* + * output the full query string during debug so we can see + * what lame error the query has. + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + /* attempt query up to 3 times. */ + for (i=0; i < 3; i++) { + qres = mysql_query((MYSQL *) dbi->dbconn, querystring); + if (qres == 0) + break; + for (j=0; mysql_ping((MYSQL *) dbi->dbconn) != 0 && j < 4; j++) + ; + } + + if (qres == 0) { + result = ISC_R_SUCCESS; + if (query != COUNTZONE) { + *rs = mysql_store_result((MYSQL *) dbi->dbconn); + if (*rs == NULL) + result = ISC_R_FAILURE; + } + } else { + result = ISC_R_FAILURE; + } + + + cleanup: + /* it's always good to cleanup after yourself */ + + /* if we couldn't even get DBI, just return NULL */ + if (dbi == NULL) + return ISC_R_FAILURE; + + /* free dbi->zone string */ + if (dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + + /* free dbi->record string */ + if (dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + + /* free dbi->client string */ + if (dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + + /* release query string */ + if (querystring != NULL) + isc_mem_free(ns_g_mctx, querystring); + + /* return result */ + return result; +} + +/*% + * The processing of result sets for lookup and authority are + * exactly the same. So that functionality has been moved + * into this function to minimize code. + */ + +static isc_result_t +mysql_process_rs(dns_sdlzlookup_t *lookup, MYSQL_RES *rs) +{ + isc_result_t result = ISC_R_NOTFOUND; + MYSQL_ROW row; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + row = mysql_fetch_row(rs); /* get a row from the result set */ + fields = mysql_num_fields(rs); /* how many columns in result set */ + while (row != NULL) { + switch(fields) { + case 1: + /* + * one column in rs, it's the data field. use + * default type of A record, and default TTL + * of 86400 + */ + result = dns_sdlz_putrr(lookup, "a", 86400, + safeGet(row[0])); + break; + case 2: + /* + * two columns, data field, and data type. + * use default TTL of 86400. + */ + result = dns_sdlz_putrr(lookup, safeGet(row[0]), 86400, + safeGet(row[1])); + break; + case 3: + /* + * three columns, all data no defaults. + * convert text to int, make sure it worked + * right. + */ + ttl = strtol(safeGet(row[0]), &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver ttl must be " + "a postive number"); + } + result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl, + safeGet(row[2])); + break; + default: + /* + * more than 3 fields, concatenate the last + * ones together. figure out how long to make + * string. + */ + for (j=2, len=0; j < fields; j++) { + len += strlen(safeGet(row[j])) + 1; + } + /* + * allocate string memory, allow for NULL to + * term string + */ + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if (tmpString == NULL) { + /* major bummer, need more ram */ + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable " + "to allocate memory for " + "temporary string"); + mysql_free_result(rs); + return (ISC_R_FAILURE); /* Yeah, I'd say! */ + } + /* copy field to tmpString */ + strcpy(tmpString, safeGet(row[2])); + + + /* + * concat the rest of fields together, space + * between each one. + */ + for (j=3; j < fields; j++) { + strcat(tmpString, " "); + strcat(tmpString, safeGet(row[j])); + } + /* convert text to int, make sure it worked right */ + ttl = strtol(safeGet(row[0]), &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver ttl must be " + "a postive number"); + } + /* ok, now tell Bind about it. */ + result = dns_sdlz_putrr(lookup, safeGet(row[1]), + ttl, tmpString); + /* done, get rid of this thing. */ + isc_mem_free(ns_g_mctx, tmpString); + } + /* I sure hope we were successful */ + if (result != ISC_R_SUCCESS) { + /* nope, get rid of the Result set, and log a msg */ + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putrr returned error. " + "Error code was: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + row = mysql_fetch_row(rs); /* get next row */ + } + + /* free result set memory */ + mysql_free_result(rs); + + /* return result code */ + return result; +} + +/* + * SDLZ interface methods + */ + +/*% determine if the zone is supported by (in) the database */ + +static isc_result_t +mysql_findzone(void *driverarg, void *dbdata, const char *name) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + my_ulonglong rows; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs); + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS || rs == NULL) { + if (rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return " + "result set for findzone query"); + return (ISC_R_FAILURE); + } + /* count how many rows in result set */ + rows = mysql_num_rows(rs); + /* get rid of result set, we are done with it. */ + mysql_free_result(rs); + + /* if we returned any rows, zone is supported. */ + if (rows > 0) { + mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL); + return (ISC_R_SUCCESS); + } + + /* no rows returned, zone is not supported. */ + return (ISC_R_NOTFOUND); +} + +/*% Determine if the client is allowed to perform a zone transfer */ +static isc_result_t +mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + my_ulonglong rows; + + UNUSED(driverarg); + + /* first check if the zone is supported by the database. */ + result = mysql_findzone(driverarg, dbdata, name); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + /* + * if we get to this point we know the zone is supported by + * the database the only questions now are is the zone + * transfer is allowed for this client and did the config file + * have an allow zone xfr query. + * + * Run our query, and get a result set from the database. + */ + result = mysql_get_resultset(name, NULL, client, ALLOWXFR, + dbdata, &rs); + /* if we get "not implemented", send it along. */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS || rs == NULL) { + if (rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return " + "result set for allow xfr query"); + return (ISC_R_FAILURE); + } + /* count how many rows in result set */ + rows = mysql_num_rows(rs); + /* get rid of result set, we are done with it. */ + mysql_free_result(rs); + + /* if we returned any rows, zone xfr is allowed. */ + if (rows > 0) + return (ISC_R_SUCCESS); + + /* no rows returned, zone xfr not allowed */ + return (ISC_R_NOPERM); +} + +/*% + * If the client is allowed to perform a zone transfer, the next order of + * business is to get all the nodes in the zone, so bind can respond to the + * query. + */ +static isc_result_t +mysql_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + MYSQL_ROW row; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs); + /* if we get "not implemented", send it along */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return " + "result set for all nodes query"); + return (ISC_R_FAILURE); + } + + result = ISC_R_NOTFOUND; + + row = mysql_fetch_row(rs); /* get a row from the result set */ + fields = mysql_num_fields(rs); /* how many columns in result set */ + while (row != NULL) { + if (fields < 4) { /* gotta have at least 4 columns */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver too few fields returned " + "by all nodes query"); + } + /* convert text to int, make sure it worked right */ + ttl = strtol(safeGet(row[0]), &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver ttl must be " + "a postive number"); + } + if (fields == 4) { + /* tell Bind about it. */ + result = dns_sdlz_putnamedrr(allnodes, safeGet(row[0]), + safeGet(row[1]), ttl, + safeGet(row[3])); + } else { + /* + * more than 4 fields, concatenate the last + * ones together. figure out how long to make + * string. + */ + for (j=3, len=0; j < fields; j++) { + len += strlen(safeGet(row[j])) + 1; + } + /* allocate memory, allow for NULL to term string */ + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if (tmpString == NULL) { /* we need more ram. */ + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable " + "to allocate memory for " + "temporary string"); + mysql_free_result(rs); + return (ISC_R_FAILURE); + } + /* copy this field to tmpString */ + strcpy(tmpString, safeGet(row[3])); + /* concatonate the rest, with spaces between */ + for (j=4; j < fields; j++) { + strcat(tmpString, " "); + strcat(tmpString, safeGet(row[j])); + } + /* tell Bind about it. */ + result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]), + safeGet(row[1]), + ttl, tmpString); + isc_mem_free(ns_g_mctx, tmpString); + } + /* if we weren't successful, log err msg */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putnamedrr returned error. " + "Error code was: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + break; + } + /* get next row from the result set */ + row = mysql_fetch_row(rs); + } + + /* free result set memory */ + mysql_free_result(rs); + + return result; +} + +/*% if the lookup function does not return SOA or NS records for the zone, + * use this function to get that information for Bind. + */ + +static isc_result_t +mysql_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs); + /* if we get "not implemented", send it along */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return " + "result set for authority query"); + return (ISC_R_FAILURE); + } + /* + * lookup and authority result sets are processed in the same + * manner mysql_process_rs does the job for both functions. + */ + return mysql_process_rs(lookup, rs); +} + +/*% if zone is supported, lookup up a (or multiple) record(s) in it */ +static isc_result_t +mysql_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + MYSQL_RES *rs = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs); + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + mysql_free_result(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver unable to return " + "result set for lookup query"); + return (ISC_R_FAILURE); + } + /* + * lookup and authority result sets are processed in the same manner + * mysql_process_rs does the job for both functions. + */ + return mysql_process_rs(lookup, rs); +} + +/*% + * create an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement which is + * passed into all query functions. + */ +static isc_result_t +mysql_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *tmp = NULL; + char *dbname = NULL; + char *host = NULL; + char *user = NULL; + char *pass = NULL; + char *socket = NULL; + int port; + MYSQL *dbc; + char *endp; + int j; + unsigned int flags = 0; + + UNUSED(driverarg); + UNUSED(dlzname); + + /* verify we have at least 4 arg's passed to the driver */ + if (argc < 4) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver requires " + "at least 4 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 8 arg's should be passed to the driver */ + if (argc > 8) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver cannot accept " + "more than 7 command line args."); + return (ISC_R_FAILURE); + } + + /* parse connection string and get paramters. */ + + /* get db name - required */ + dbname = getParameterValue(argv[1], "dbname="); + if (dbname == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver requires a dbname parameter."); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + /* get db port. Not required, but must be > 0 if specified */ + tmp = getParameterValue(argv[1], "port="); + if (tmp == NULL) { + port = 0; + } else { + port = strtol(tmp, &endp, 10); + if (*endp != '\0' || port < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Mysql driver port " + "must be a positive number."); + isc_mem_free(ns_g_mctx, tmp); + result = ISC_R_FAILURE; + goto full_cleanup; + } + isc_mem_free(ns_g_mctx, tmp); + } + + /* how many queries were passed in from config file? */ + switch(argc) { + case 4: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL, + argv[2], argv[3], NULL, &dbi); + break; + case 5: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[4], + argv[2], argv[3], NULL, &dbi); + break; + case 6: + result = build_sqldbinstance(ns_g_mctx, argv[5], NULL, argv[4], + argv[2], argv[3], NULL, &dbi); + break; + case 7: + result = build_sqldbinstance(ns_g_mctx, argv[5], + argv[6], argv[4], + argv[2], argv[3], NULL, &dbi); + break; + case 8: + result = build_sqldbinstance(ns_g_mctx, argv[5], + argv[6], argv[4], + argv[2], argv[3], argv[7], &dbi); + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + /* unsuccessful?, log err msg and cleanup. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver could not create " + "database instance object."); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + /* create and set db connection */ + dbi->dbconn = mysql_init(NULL); + + /* if db connection cannot be created, log err msg and cleanup. */ + if (dbi->dbconn == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver could not allocate " + "memory for database connection"); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + tmp = getParameterValue(argv[1], "compress="); + if (tmp != NULL) { + if (strcasecmp(tmp, "true") == 0) + flags = CLIENT_COMPRESS; + isc_mem_free(ns_g_mctx, tmp); + } + + tmp = getParameterValue(argv[1], "ssl="); + if (tmp != NULL) { + if (strcasecmp(tmp, "true") == 0) + flags = flags | CLIENT_SSL; + isc_mem_free(ns_g_mctx, tmp); + } + + tmp = getParameterValue(argv[1], "space="); + if (tmp != NULL) { + if (strcasecmp(tmp, "ignore") == 0) + flags = flags | CLIENT_IGNORE_SPACE; + isc_mem_free(ns_g_mctx, tmp); + } + + dbc = NULL; + host = getParameterValue(argv[1], "host="); + user = getParameterValue(argv[1], "user="); + pass = getParameterValue(argv[1], "pass="); + socket = getParameterValue(argv[1], "socket="); + + for (j=0; dbc == NULL && j < 4; j++) + dbc = mysql_real_connect((MYSQL *) dbi->dbconn, host, + user, pass, dbname, port, socket, + flags); + + /* let user know if we couldn't connect. */ + if (dbc == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "mysql driver failed to create " + "database connection after 4 attempts"); + result = ISC_R_FAILURE; + goto full_cleanup; + } + + /* return db connection via dbdata */ + *dbdata = dbi; + + result = ISC_R_SUCCESS; + goto cleanup; + + full_cleanup: + + destroy_sqldbinstance(dbi); + + cleanup: + + if (dbname != NULL) + isc_mem_free(ns_g_mctx, dbname); + if (host != NULL) + isc_mem_free(ns_g_mctx, host); + if (user != NULL) + isc_mem_free(ns_g_mctx, user); + if (pass != NULL) + isc_mem_free(ns_g_mctx, pass); + if (socket != NULL) + isc_mem_free(ns_g_mctx, socket); + + + return result; +} + +/*% + * destroy the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement. + * so we really only need to clean it up since we are not using driverarg. + */ + +static void +mysql_destroy(void *driverarg, void *dbdata) +{ + dbinstance_t *dbi; + + UNUSED(driverarg); + + dbi = (dbinstance_t *) dbdata; + + /* release DB connection */ + if (dbi->dbconn != NULL) + mysql_close((MYSQL *) dbi->dbconn); + + /* destroy DB instance */ + destroy_sqldbinstance(dbi); +} + +/* pointers to all our runtime methods. */ +/* this is used during driver registration */ +/* i.e. in dlz_mysql_init below. */ +static dns_sdlzmethods_t dlz_mysql_methods = { + mysql_create, + mysql_destroy, + mysql_findzone, + mysql_lookup, + mysql_authority, + mysql_allnodes, + mysql_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_mysql_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ mysql driver."); + + /* Driver is always threadsafe. Because of the way MySQL handles + * threads the MySQL driver can only be used when bind is run single + * threaded. Using MySQL with Bind running multi-threaded is not + * allowed. When using the MySQL driver "-n1" should always be + * passed to Bind to guarantee single threaded operation. + */ + result = dns_sdlzregister("mysql", &dlz_mysql_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + ns_g_mctx, &dlz_mysql); + /* if we can't register the driver, there are big problems. */ + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_mysql_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ mysql driver."); + + /* unregister the driver. */ + if (dlz_mysql != NULL) + dns_sdlzunregister(&dlz_mysql); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_odbc_driver.c b/contrib/dlz/drivers/dlz_odbc_driver.c new file mode 100644 index 00000000000..b0ba115495c --- /dev/null +++ b/contrib/dlz/drivers/dlz_odbc_driver.c @@ -0,0 +1,1568 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_ODBC + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +static dns_sdlzimplementation_t *dlz_odbc = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +#define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0) + +/* + * Private Structures + */ + +/* + * structure to hold ODBC connection & statement + */ + +typedef struct{ + SQLHDBC dbc; + SQLHSTMT stmnt; +} odbc_db_t; + +/* + * Structure to hold everthing needed by this "instance" of the odbc driver + * remember, the driver code is only loaded once, but may have many separate + * instances + */ + +typedef struct { + +#ifdef ISC_PLATFORM_USETHREADS + + db_list_t *db; /* handle to a list of DB */ + +#else + + dbinstance_t *db; /* handle to db */ + +#endif + + SQLHENV sql_env; /* handle to SQL environment */ + SQLCHAR *dsn; + SQLCHAR *user; + SQLCHAR *pass; +} odbc_instance_t; + +/* forward reference */ + +static size_t +odbc_makesafe(char *to, const char *from, size_t length); + +/* + * Private methods + */ + +static SQLSMALLINT +safeLen(void *a) { + if (a == NULL) + return 0; + return strlen((char *) a); +} + +/*% propertly cleans up an odbc_instance_t */ + +static void +destroy_odbc_instance(odbc_instance_t *odbc_inst) { + +#ifdef ISC_PLATFORM_USETHREADS + + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + /* get the first DBI in the list */ + ndbi = ISC_LIST_HEAD(*odbc_inst->db); + + /* loop through the list */ + while (ndbi != NULL) { + dbi = ndbi; + /* get the next DBI in the list */ + ndbi = ISC_LIST_NEXT(dbi, link); + + /* if we have a connection / statement object in memory */ + if (dbi->dbconn != NULL) { + /* free statement handle */ + if (((odbc_db_t *) (dbi->dbconn))->stmnt != NULL) { + SQLFreeHandle(SQL_HANDLE_STMT, + ((odbc_db_t *) + (dbi->dbconn))->stmnt); + ((odbc_db_t *) (dbi->dbconn))->stmnt = NULL; + } + + /* disconnect from database & free connection handle */ + if (((odbc_db_t *) (dbi->dbconn))->dbc != NULL) { + SQLDisconnect(((odbc_db_t *) + dbi->dbconn)->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, + ((odbc_db_t *) + (dbi->dbconn))->dbc); + ((odbc_db_t *) (dbi->dbconn))->dbc = NULL; + } + + /* free memory that held connection & statement. */ + isc_mem_free(ns_g_mctx, dbi->dbconn); + } + /* release all memory that comprised a DBI */ + destroy_sqldbinstance(dbi); + } + /* release memory for the list structure */ + isc_mem_put(ns_g_mctx, odbc_inst->db, sizeof(db_list_t)); + +#else /* ISC_PLATFORM_USETHREADS */ + + /* free statement handle */ + if (((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt != NULL) { + SQLFreeHandle(SQL_HANDLE_STMT, + ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt); + ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt = NULL; + } + + /* disconnect from database, free connection handle */ + if (((odbc_db_t *) (odbc_inst->db->dbconn))->dbc != NULL) { + SQLDisconnect(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, + ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc); + ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc = NULL; + } + /* free mem for the odbc_db_t structure held in db */ + if (((odbc_db_t *) odbc_inst->db->dbconn) != NULL) { + isc_mem_free(ns_g_mctx, odbc_inst->db->dbconn); + odbc_inst->db->dbconn = NULL; + } + + if (odbc_inst->db != NULL) + destroy_sqldbinstance(odbc_inst->db); + +#endif /* ISC_PLATFORM_USETHREADS */ + + + /* free sql environment */ + if (odbc_inst->sql_env != NULL) + SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env); + + /* free ODBC instance strings */ + if (odbc_inst->dsn != NULL) + isc_mem_free(ns_g_mctx, odbc_inst->dsn); + if (odbc_inst->pass != NULL) + isc_mem_free(ns_g_mctx, odbc_inst->pass); + if (odbc_inst->user != NULL) + isc_mem_free(ns_g_mctx, odbc_inst->user); + + /* free memory for odbc_inst */ + if (odbc_inst != NULL) + isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t)); + +} + +/*% Connects to database, and creates ODBC statements */ + +static isc_result_t +odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc) { + + odbc_db_t *ndb = *dbc; + SQLRETURN sqlRes; + isc_result_t result = ISC_R_SUCCESS; + + if (ndb != NULL) { + /* + * if db != null, we have to do some cleanup + * if statement handle != null free it + */ + if (ndb->stmnt != NULL) { + SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); + ndb->stmnt = NULL; + } + + /* if connection handle != null free it */ + if (ndb->dbc != NULL) { + SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); + ndb->dbc = NULL; + } + } else { + ndb = isc_mem_allocate(ns_g_mctx, sizeof(odbc_db_t)); + if (ndb == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to allocate memory"); + return ISC_R_NOMEMORY; + } + memset(ndb, 0, sizeof(odbc_db_t)); + } + + sqlRes = SQLAllocHandle(SQL_HANDLE_DBC, dbi->sql_env, &(ndb->dbc)); + if (!sqlOK(sqlRes)) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to allocate memory"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + + sqlRes = SQLConnect(ndb->dbc, dbi->dsn, safeLen(dbi->dsn), dbi->user, + safeLen(dbi->user), dbi->pass, safeLen(dbi->pass)); + if (!sqlOK(sqlRes)) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to connect"); + result = ISC_R_FAILURE; + goto cleanup; + } + + sqlRes = SQLAllocHandle(SQL_HANDLE_STMT, ndb->dbc, &(ndb->stmnt)); + if (!sqlOK(sqlRes)) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to allocate memory"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + + *dbc = ndb; + + return ISC_R_SUCCESS; + + cleanup: + + if (ndb != NULL) { + + /* if statement handle != null free it */ + if (ndb->stmnt != NULL) { + SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); + ndb->stmnt = NULL; + } + + /* if connection handle != null free it */ + if (ndb->dbc != NULL) { + SQLDisconnect(ndb->dbc); + SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); + ndb->dbc = NULL; + } + /* free memory holding ndb */ + isc_mem_free(ns_g_mctx, ndb); + } + + return result; +} + +/*% + * Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ + +#ifdef ISC_PLATFORM_USETHREADS + +static dbinstance_t * +odbc_find_avail_conn(db_list_t *dblist) +{ + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + /* get top of list */ + head = dbi = ISC_LIST_HEAD(*dblist); + + /* loop through list */ + while (count < dbc_search_limit) { + /* try to lock on the mutex */ + if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) + return dbi; /* success, return the DBI for use. */ + + /* not successful, keep trying */ + dbi = ISC_LIST_NEXT(dbi, link); + + /* check to see if we have gone to the top of the list. */ + if (dbi == NULL) { + count++; + dbi = head; + } + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Odbc driver unable to find available " + "connection after searching %d times", + count); + return NULL; +} + +#endif /* ISC_PLATFORM_USETHREADS */ + +/*% Allocates memory for a new string, and then constructs the new + * string by "escaping" the input string. The new string is + * safe to be used in queries. This is necessary because we cannot + * be sure of what types of strings are passed to us, and we don't + * want special characters in the string causing problems. + */ + +static char * +odbc_escape_string(const char *instr) { + + char *outstr; + unsigned int len; + + if (instr == NULL) + return NULL; + + len = strlen(instr); + + outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); + if (outstr == NULL) + return NULL; + + odbc_makesafe(outstr, instr, len); + + return outstr; +} + +/* --------------- + * Escaping arbitrary strings to get valid SQL strings/identifiers. + * + * Replaces "\\" with "\\\\" and "'" with "''". + * length is the length of the buffer pointed to by + * from. The buffer at to must be at least 2*length + 1 characters + * long. A terminating NUL character is written. + * + * NOTICE!!! + * This function was borrowed directly from PostgreSQL's libpq. + * + * The copyright statements from the original file containing this + * function are included below: + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * --------------- + */ + +static size_t +odbc_makesafe(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +/*% + * This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. The data base instance that is used is returned + * to the caller so they can get the data from the result set from it. + * If successfull, it will be the responsibility of the caller to close + * the cursor, and unlock the mutex of the DBI when they are done with it. + * If not successfull, this function will perform all the cleanup. + */ + + +static isc_result_t +odbc_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, dbinstance_t **r_dbi) +{ + + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + unsigned int j = 0; + SQLRETURN sqlRes; + + REQUIRE(*r_dbi == NULL); + + /* get db instance / connection */ +#ifdef ISC_PLATFORM_USETHREADS + + /* find an available DBI from the list */ + dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db); + +#else /* ISC_PLATFORM_USETHREADS */ + + /* + * only 1 DBI - no need to lock instance lock either + * only 1 thread in the whole process, no possible contention. + */ + dbi = (dbinstance_t *) ((odbc_instance_t *) dbdata)->db; + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* if DBI is null, can't do anything else */ + if (dbi == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + /* what type of query are we going to run? */ + switch(query) { + case ALLNODES: + /* + * if the query was not passed in from the config file + * then we can't run it. return not_implemented, so + * it's like the code for that operation was never + * built into the driver.... AHHH flexibility!!! + */ + if (dbi->allnodes_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case ALLOWXFR: + /* same as comments as ALLNODES */ + if (dbi->allowxfr_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case AUTHORITY: + /* same as comments as ALLNODES */ + if (dbi->authority_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case FINDZONE: + /* this is required. It's the whole point of DLZ! */ + if (dbi->findzone_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. " + "Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + case LOOKUP: + /* this is required. It's also a major point of DLZ! */ + if (dbi->lookup_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. " + "Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "odbc_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + + /* + * was a zone string passed? If so, make it safe for use in + * queries. + */ + if (zone != NULL) { + dbi->zone = odbc_escape_string(zone); + if (dbi->zone == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->zone = NULL; + } + + /* + * was a record string passed? If so, make it safe for use in + * queries. + */ + if (record != NULL) { + dbi->record = odbc_escape_string(record); + if (dbi->record == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->record = NULL; + } + + /* + * was a client string passed? If so, make it safe for use in + * queries. + */ + if (client != NULL) { + dbi->client = odbc_escape_string(client); + if (dbi->client == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->client = NULL; + } + + /* + * what type of query are we going to run? + * this time we build the actual query to run. + */ + switch(query) { + case ALLNODES: + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + break; + case ALLOWXFR: + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + break; + case AUTHORITY: + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + break; + case FINDZONE: + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + break; + case LOOKUP: + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "odbc_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ + if (querystring == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* output the full query string during debug so we can see */ + /* what lame error the query has. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + /* attempt query up to 3 times. */ + for (j=0; j < 3; j++) { + /* try to get result set */ + sqlRes = SQLExecDirect(((odbc_db_t *) dbi->dbconn)->stmnt, + (SQLCHAR *) querystring, + (SQLINTEGER) strlen(querystring)); + + /* if error, reset DB connection */ + if (!sqlOK(sqlRes)) { + /* close cursor */ + SQLCloseCursor(((odbc_db_t *) dbi->dbconn)->stmnt); + /* attempt to reconnect */ + result = odbc_connect((odbc_instance_t *) dbdata, + (odbc_db_t **) &(dbi->dbconn)); + /* check if we reconnected */ + if (result != ISC_R_SUCCESS) + break; + /* incase this is the last time through the loop */ + result = ISC_R_FAILURE; + } else { + result = ISC_R_SUCCESS; + /* return dbi */ + *r_dbi = dbi; + /* result set ok, break loop */ + break; + } + } /* end for loop */ + + cleanup: /* it's always good to cleanup after yourself */ + + /* if we couldn't even allocate DBI, just return NULL */ + if (dbi == NULL) + return ISC_R_FAILURE; + + /* free dbi->zone string */ + if (dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + + /* free dbi->record string */ + if (dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + + /* free dbi->client string */ + if (dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + +#ifdef ISC_PLATFORM_USETHREADS + + /* if we are done using this dbi, release the lock */ + if (result != ISC_R_SUCCESS) + isc_mutex_unlock(&dbi->instance_lock); + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* release query string */ + if (querystring != NULL) + isc_mem_free(ns_g_mctx, querystring ); + + /* return result */ + return result; + +} + +/*% + * Gets a single field from the ODBC statement. The memory for the + * returned data is dynamically allocated. If this method is successful + * it is the reponsibility of the caller to free the memory using + * isc_mem_free(ns_g_mctx, *ptr); + */ + +static isc_result_t +odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data) { + + SQLINTEGER size; + + REQUIRE(data != NULL && *data == NULL); + + if (sqlOK(SQLColAttribute(stmnt, field, SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &size)) && size > 0) { + *data = isc_mem_allocate(ns_g_mctx, size + 1); + if (data != NULL) { + if (sqlOK(SQLGetData(stmnt, field, SQL_C_CHAR, + *data, size + 1,&size))) + return ISC_R_SUCCESS; + isc_mem_free(ns_g_mctx, *data); + } + } + return ISC_R_FAILURE; +} + +/*% + * Gets multiple fields from the ODBC statement. The memory for the + * returned data is dynamically allocated. If this method is successful + * it is the reponsibility of the caller to free the memory using + * isc_mem_free(ns_g_mctx, *ptr); + */ + +static isc_result_t +odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField, + SQLSMALLINT endField, char **retData) { + + isc_result_t result; + SQLINTEGER size; + int totSize = 0; + SQLSMALLINT i; + int j = 0; + char *data; + + REQUIRE(retData != NULL && *retData == NULL); + REQUIRE(startField > 0 && startField <= endField); + + /* determine how large the data is */ + for (i=startField; i <= endField; i++) + if (sqlOK(SQLColAttribute(stmnt, i, SQL_DESC_DISPLAY_SIZE, + NULL, 0, NULL, &size)) && size > 0) { + /* always allow for a " " (space) character */ + totSize += (size + 1); + /* after the data item */ + } + + if (totSize < 1) + return ISC_R_FAILURE; + + /* allow for a "\n" at the end of the string/ */ + data = isc_mem_allocate(ns_g_mctx, ++totSize); + if (data == NULL) + return ISC_R_NOMEMORY; + + result = ISC_R_FAILURE; + + /* get the data and concat all fields into a large string */ + for (i=startField; i <= endField; i++) { + if (sqlOK(SQLGetData(stmnt, i, SQL_C_CHAR, &(data[j]), + totSize - j, &size))) { + if (size > 0) { + j += size; + data[j++] = ' '; + data[j] = '\0'; + result = ISC_R_SUCCESS; + } + } else { + isc_mem_free(ns_g_mctx, data); + return ISC_R_FAILURE; + } + } + + if (result != ISC_R_SUCCESS) { + isc_mem_free(ns_g_mctx, data); + return result; + } + + *retData = data; + return ISC_R_SUCCESS; + +} + +/*% + * The processing of result sets for lookup and authority are + * exactly the same. So that functionality has been moved + * into this function to minimize code. + */ + +static isc_result_t +odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *dbi) +{ + + + isc_result_t result; + SQLSMALLINT fields; + SQLHSTMT *stmnt; + char *ttl_s; + char *type; + char *data; + char *endp; + int ttl; + + REQUIRE(dbi != NULL); + + stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt; + + /* get number of columns */ + if (!sqlOK(SQLNumResultCols(stmnt, &fields))) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to process result set"); + result = ISC_R_FAILURE; + goto process_rs_cleanup; + } + + /* get things ready for processing */ + result = ISC_R_FAILURE; + + while (sqlOK(SQLFetch(stmnt))) { + + /* set to null for next pass through */ + data = type = ttl_s = NULL; + + switch(fields) { + case 1: + /* + * one column in rs, it's the data field. use + * default type of A record, and default TTL + * of 86400. attempt to get data, & tell bind + * about it. + */ + if ((result = odbc_getField(stmnt, 1, + &data)) == ISC_R_SUCCESS) { + result = dns_sdlz_putrr(lookup, "a", + 86400, data); + } + break; + case 2: + /* + * two columns, data field, and data type. + * use default TTL of 86400. attempt to get + * DNS type & data, then tell bind about it. + */ + if ((result = odbc_getField(stmnt, 1, + &type)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 2, + &data)) == ISC_R_SUCCESS) { + result = dns_sdlz_putrr(lookup, type, + 86400, data); + } + break; + default: + /* + * 3 fields or more, concatenate the last ones + * together. attempt to get DNS ttl, type, + * data then tell Bind about them. + */ + if ((result = odbc_getField(stmnt, 1, &ttl_s)) + == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 2, &type)) + == ISC_R_SUCCESS && + (result = odbc_getManyFields(stmnt, 3, + fields, &data)) + == ISC_R_SUCCESS) { + /* try to convert ttl string to int */ + ttl = strtol(ttl_s, &endp, 10); + /* failure converting ttl. */ + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, + ISC_LOG_ERROR, + "Odbc driver ttl must " + "be a postive number"); + result = ISC_R_FAILURE; + } else { + /* + * successful converting TTL, + * tell Bind everything + */ + result = dns_sdlz_putrr(lookup, type, + ttl, data); + } + } /* closes bid if () */ + } /* closes switch(fields) */ + + /* clean up mem */ + if (ttl_s != NULL) + isc_mem_free(ns_g_mctx, ttl_s); + if (type != NULL) + isc_mem_free(ns_g_mctx, type); + if (data != NULL) + isc_mem_free(ns_g_mctx, data); + + /* I sure hope we were successful */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putrr returned error. " + "Error code was: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + goto process_rs_cleanup; + } + } /* closes while loop */ + + process_rs_cleanup: + + /* close cursor */ + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + /* free lock on dbi so someone else can use it. */ + isc_mutex_unlock(&dbi->instance_lock); + +#endif + + return result; +} + +/* + * SDLZ interface methods + */ + +/*% determine if the zone is supported by (in) the database */ + +static isc_result_t +odbc_findzone(void *driverarg, void *dbdata, const char *name) +{ + + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + /* if result != ISC_R_SUCCESS cursor and mutex already cleaned up. */ + /* so we don't have to do it here. */ + result = odbc_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &dbi); + + /* Check that we got a result set with data */ + if (result == ISC_R_SUCCESS && + !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) { + result = ISC_R_NOTFOUND; + } + + if (dbi != NULL) { + /* get rid of result set, we are done with it. */ + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + /* free lock on dbi so someone else can use it. */ + isc_mutex_unlock(&dbi->instance_lock); +#endif + } + + return result; +} + +/*% Determine if the client is allowed to perform a zone transfer */ +static isc_result_t +odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + /* first check if the zone is supported by the database. */ + result = odbc_findzone(driverarg, dbdata, name); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + /* + * if we get to this point we know the zone is supported by + * the database. the only questions now are is the zone + * transfer is allowed for this client and did the config file + * have an allow zone xfr query + * + * Run our query, and get a result set from the database. if + * result != ISC_R_SUCCESS cursor and mutex already cleaned + * up, so we don't have to do it here. + */ + result = odbc_get_resultset(name, NULL, client, ALLOWXFR, + dbdata, &dbi); + + /* if we get "not implemented", send it along. */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + + /* Check that we got a result set with data */ + if (result == ISC_R_SUCCESS && + !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) { + result = ISC_R_NOPERM; + } + + if (dbi != NULL) { + /* get rid of result set, we are done with it. */ + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + /* free lock on dbi so someone else can use it. */ + isc_mutex_unlock(&dbi->instance_lock); +#endif + + } + + return result; +} + +/*% + * If the client is allowed to perform a zone transfer, the next order of + * business is to get all the nodes in the zone, so bind can respond to the + * query. + */ + +static isc_result_t +odbc_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + + isc_result_t result; + dbinstance_t *dbi = NULL; + SQLHSTMT *stmnt; + SQLSMALLINT fields; + char *data; + char *type; + char *ttl_s; + int ttl; + char *host; + char *endp; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = odbc_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &dbi); + + /* if we get "not implemented", send it along */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to return " + "result set for all nodes query"); + return (ISC_R_FAILURE); + } + + stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt; + + /* get number of columns */ + if (!sqlOK(SQLNumResultCols(stmnt, &fields))) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to process result set"); + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + if (fields < 4) { /* gotta have at least 4 columns */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver too few fields returned by " + "all nodes query"); + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + /* get things ready for processing */ + result = ISC_R_FAILURE; + + while (sqlOK(SQLFetch(stmnt))) { + + /* set to null for next pass through */ + data = host = type = ttl_s = NULL; + + /* + * attempt to get DNS ttl, type, host, data then tell + * Bind about them + */ + if ((result = odbc_getField(stmnt, 1, + &ttl_s)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 2, + &type)) == ISC_R_SUCCESS && + (result = odbc_getField(stmnt, 3, + &host)) == ISC_R_SUCCESS && + (result = odbc_getManyFields(stmnt, 4, fields, + &data)) == ISC_R_SUCCESS) { + /* convert ttl string to int */ + ttl = strtol(ttl_s, &endp, 10); + /* failure converting ttl. */ + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver ttl must be " + "a postive number"); + result = ISC_R_FAILURE; + } else { + /* successful converting TTL, tell Bind */ + result = dns_sdlz_putnamedrr(allnodes, host, + type, ttl, data); + } + } /* closes big if () */ + + /* clean up mem */ + if (ttl_s != NULL) + isc_mem_free(ns_g_mctx, ttl_s); + if (type != NULL) + isc_mem_free(ns_g_mctx, type); + if (host != NULL) + isc_mem_free(ns_g_mctx, host); + if (data != NULL) + isc_mem_free(ns_g_mctx, data); + + /* if we weren't successful, log err msg */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putnamedrr returned error. " + "Error code was: %s", + isc_result_totext(result)); + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + } /* closes while loop */ + + allnodes_cleanup: + + /* close cursor */ + SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); + +#ifdef ISC_PLATFORM_USETHREADS + + /* free lock on dbi so someone else can use it. */ + isc_mutex_unlock(&dbi->instance_lock); + +#endif + + return result; +} + +/*% + * if the lookup function does not return SOA or NS records for the zone, + * use this function to get that information for Bind. + */ + +static isc_result_t +odbc_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = odbc_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &dbi); + /* if we get "not implemented", send it along */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to return " + "result set for authority query"); + return (ISC_R_FAILURE); + } + /* lookup and authority result sets are processed in the same manner */ + /* odbc_process_rs does the job for both functions. */ + return odbc_process_rs(lookup, dbi); +} + +/*% if zone is supported, lookup up a (or multiple) record(s) in it */ + +static isc_result_t +odbc_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = odbc_get_resultset(zone, name, NULL, LOOKUP, dbdata, &dbi); + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver unable to return " + "result set for lookup query"); + return (ISC_R_FAILURE); + } + /* lookup and authority result sets are processed in the same manner */ + /* odbc_process_rs does the job for both functions. */ + return odbc_process_rs(lookup, dbi); +} + +/*% + * create an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement which is + * passed into all query functions. + */ +static isc_result_t +odbc_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + odbc_instance_t *odbc_inst = NULL; + dbinstance_t *db = NULL; + SQLRETURN sqlRes; + +#ifdef ISC_PLATFORM_USETHREADS + /* if multi-threaded, we need a few extra variables. */ + int dbcount; + int i; + char *endp; + +#endif /* ISC_PLATFORM_USETHREADS */ + + UNUSED(dlzname); + UNUSED(driverarg); + +#ifdef ISC_PLATFORM_USETHREADS + /* if debugging, let user know we are multithreaded. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Odbc driver running multithreaded"); +#else /* ISC_PLATFORM_USETHREADS */ + /* if debugging, let user know we are single threaded. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Odbc driver running single threaded"); +#endif /* ISC_PLATFORM_USETHREADS */ + + /* verify we have at least 5 arg's passed to the driver */ + if (argc < 5) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver requires at least " + "4 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 8 arg's should be passed to the driver */ + if (argc > 8) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver cannot accept more than " + "7 command line args."); + return (ISC_R_FAILURE); + } + + /* multithreaded build can have multiple DB connections */ +#ifdef ISC_PLATFORM_USETHREADS + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver database connection count " + "must be positive."); + return (ISC_R_FAILURE); + } + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* allocate memory for odbc instance */ + odbc_inst = isc_mem_get(ns_g_mctx, sizeof(odbc_instance_t)); + if (odbc_inst == NULL) + return (ISC_R_NOMEMORY); + memset(odbc_inst, 0, sizeof(odbc_instance_t)); + + /* parse connection string and get paramters. */ + + /* get odbc database dsn - required */ + odbc_inst->dsn = (SQLCHAR *) getParameterValue(argv[2], + "dsn="); + if (odbc_inst->dsn == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "odbc driver requires a dns parameter."); + result = ISC_R_FAILURE; + goto cleanup; + } + /* get odbc database username */ + /* if no username was passed, set odbc_inst.user = NULL; */ + odbc_inst->user = (SQLCHAR *) getParameterValue(argv[2], + "user="); + + /* get odbc database password */ + /* if no password was passed, set odbc_inst.pass = NULL; */ + odbc_inst->pass = (SQLCHAR *) getParameterValue(argv[2], "pass="); + + /* create odbc environment & set environment to ODBC V3 */ + if (odbc_inst->sql_env == NULL) { + /* create environment handle */ + sqlRes = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, + &(odbc_inst->sql_env)); + if (!sqlOK(sqlRes)) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Odbc driver unable to allocate memory"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + /*set ODBC version = 3 */ + sqlRes = SQLSetEnvAttr(odbc_inst->sql_env, + SQL_ATTR_ODBC_VERSION, + (void *) SQL_OV_ODBC3, 0); + if (!sqlOK(sqlRes)) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Unable to configure ODBC environment"); + result = ISC_R_NOMEMORY; + goto cleanup; + } + } + +#ifdef ISC_PLATFORM_USETHREADS + + /* allocate memory for database connection list */ + odbc_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); + if (odbc_inst->db == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + + /* initialize DB connection list */ + ISC_LIST_INIT(*odbc_inst->db); + + /* create the appropriate number of database instances (DBI) */ + /* append each new DBI to the end of the list */ + for (i=0; i < dbcount; i++) { + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* how many queries were passed in from config file? */ + switch(argc) { + case 5: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, + NULL, argv[3], argv[4], + NULL, &db); + break; + case 6: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, + argv[5], argv[3], argv[4], + NULL, &db); + break; + case 7: + result = build_sqldbinstance(ns_g_mctx, argv[6], NULL, + argv[5], argv[3], argv[4], + NULL, &db); + break; + case 8: + result = build_sqldbinstance(ns_g_mctx, argv[6], + argv[7], argv[5], argv[3], + argv[4], NULL, &db); + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + /* unsuccessful?, log err msg and cleanup. */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver could not create " + "database instance object."); + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + + /* when multithreaded, build a list of DBI's */ + ISC_LINK_INIT(db, link); + ISC_LIST_APPEND(*odbc_inst->db, db, link); + +#endif + + result = odbc_connect(odbc_inst, (odbc_db_t **) &(db->dbconn)); + + if (result != ISC_R_SUCCESS) { + +#ifdef ISC_PLATFORM_USETHREADS + + /* + * if multi threaded, let user know which + * connection failed. user could be + * attempting to create 10 db connections and + * for some reason the db backend only allows + * 9. + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver failed to create database " + "connection number %u after 3 attempts", + i+1); +#else + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Odbc driver failed to create database " + "connection after 3 attempts"); +#endif + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + + /* set DB = null for next loop through. */ + db = NULL; + + } /* end for loop */ + +#else + /* tell odbc_inst about the db connection we just created. */ + odbc_inst->db = db; + +#endif + + /* set dbdata to the odbc_instance we created. */ + *dbdata = odbc_inst; + + /* hey, we got through all of that ok, return success. */ + return(ISC_R_SUCCESS); + + cleanup: + + destroy_odbc_instance(odbc_inst); + + return result; +} + +/*% + * destroy an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement. + * so we really only need to clean it up since we are not using driverarg. + */ + +static void +odbc_destroy(void *driverarg, void *dbdata) +{ + UNUSED(driverarg); + + destroy_odbc_instance((odbc_instance_t *) dbdata); +} + + +/* pointers to all our runtime methods. */ +/* this is used during driver registration */ +/* i.e. in dlz_odbc_init below. */ +static dns_sdlzmethods_t dlz_odbc_methods = { + odbc_create, + odbc_destroy, + odbc_findzone, + odbc_lookup, + odbc_authority, + odbc_allnodes, + odbc_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_odbc_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ odbc driver."); + + /* + * Driver is always threadsafe. When multithreaded all + * functions use multithreaded code. When not multithreaded, + * all functions can only be entered once, but only 1 thread + * of operation is available in Bind. So everything is still + * threadsafe. + */ + result = dns_sdlzregister("odbc", &dlz_odbc_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + ns_g_mctx, &dlz_odbc); + /* if we can't register the driver, there are big problems. */ + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_odbc_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ odbc driver."); + + /* unregister the driver. */ + if (dlz_odbc != NULL) + dns_sdlzunregister(&dlz_odbc); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_postgres_driver.c b/contrib/dlz/drivers/dlz_postgres_driver.c new file mode 100644 index 00000000000..7f3b2920f66 --- /dev/null +++ b/contrib/dlz/drivers/dlz_postgres_driver.c @@ -0,0 +1,1342 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_POSTGRES + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* temporarily include time. */ +#include + +#include + +static dns_sdlzimplementation_t *dlz_postgres = NULL; + +#define dbc_search_limit 30 +#define ALLNODES 1 +#define ALLOWXFR 2 +#define AUTHORITY 3 +#define FINDZONE 4 +#define LOOKUP 5 + +/* + * Private methods + */ + +/* --------------- + * Escaping arbitrary strings to get valid SQL strings/identifiers. + * + * Replaces "\\" with "\\\\" and "'" with "''". + * length is the length of the buffer pointed to by + * from. The buffer at to must be at least 2*length + 1 characters + * long. A terminating NUL character is written. + * + * NOTICE!!! + * This function was borrowed directly from PostgreSQL's libpq. + * The function was originally called PQescapeString and renamed + * to postgres_makesafe to avoid a naming collision. + * PQescapeString is a new function made available in Postgres 7.2. + * For some reason the function is not properly exported on Win32 + * builds making the function unavailable on Windows. Also, since + * this function is new it would require building this driver with + * the libpq 7.2. By borrowing this function the Windows problem + * is solved, and the dependence on libpq 7.2 is removed. Libpq is + * still required of course, but an older version should work now too. + * + * The copyright statements from the original file containing this + * function are included below: + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * --------------- + */ + +static size_t +postgres_makesafe(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +#ifdef ISC_PLATFORM_USETHREADS + +/*% + * Properly cleans up a list of database instances. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ +static void +postgres_destroy_dblist(db_list_t *dblist) +{ + + dbinstance_t *ndbi = NULL; + dbinstance_t *dbi = NULL; + + /* get the first DBI in the list */ + ndbi = ISC_LIST_HEAD(*dblist); + + /* loop through the list */ + while (ndbi != NULL) { + dbi = ndbi; + /* get the next DBI in the list */ + ndbi = ISC_LIST_NEXT(dbi, link); + /* release DB connection */ + if (dbi->dbconn != NULL) + PQfinish((PGconn *) dbi->dbconn); + /* release all memory that comprised a DBI */ + destroy_sqldbinstance(dbi); + } + /* release memory for the list structure */ + isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t)); +} + +/*% + * Loops through the list of DB instances, attempting to lock + * on the mutex. If successful, the DBI is reserved for use + * and the thread can perform queries against the database. + * If the lock fails, the next one in the list is tried. + * looping continues until a lock is obtained, or until + * the list has been searched dbc_search_limit times. + * This function is only used when the driver is compiled for + * multithreaded operation. + */ + +static dbinstance_t * +postgres_find_avail_conn(db_list_t *dblist) +{ + dbinstance_t *dbi = NULL; + dbinstance_t *head; + int count = 0; + + /* get top of list */ + head = dbi = ISC_LIST_HEAD(*dblist); + + /* loop through list */ + while (count < dbc_search_limit) { + /* try to lock on the mutex */ + if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) + return dbi; /* success, return the DBI for use. */ + + /* not successful, keep trying */ + dbi = ISC_LIST_NEXT(dbi, link); + + /* check to see if we have gone to the top of the list. */ + if (dbi == NULL) { + count++; + dbi = head; + } + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Postgres driver unable to find available connection " + "after searching %d times", + count); + return NULL; +} + +#endif /* ISC_PLATFORM_USETHREADS */ + +/*% + * Allocates memory for a new string, and then constructs the new + * string by "escaping" the input string. The new string is + * safe to be used in queries. This is necessary because we cannot + * be sure of what types of strings are passed to us, and we don't + * want special characters in the string causing problems. + */ + +static char * +postgres_escape_string(const char *instr) { + + char *outstr; + unsigned int len; + + if (instr == NULL) + return NULL; + + len = strlen(instr); + + outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); + if (outstr == NULL) + return NULL; + + postgres_makesafe(outstr, instr, len); + /* PQescapeString(outstr, instr, len); */ + + return outstr; +} + +/*% + * This function is the real core of the driver. Zone, record + * and client strings are passed in (or NULL is passed if the + * string is not available). The type of query we want to run + * is indicated by the query flag, and the dbdata object is passed + * passed in to. dbdata really holds either: + * 1) a list of database instances (in multithreaded mode) OR + * 2) a single database instance (in single threaded mode) + * The function will construct the query and obtain an available + * database instance (DBI). It will then run the query and hopefully + * obtain a result set. Postgres is nice, in that once the result + * set is returned, we can make the db connection available for another + * thread to use, while this thread continues on. So, the DBI is made + * available ASAP by unlocking the instance_lock after we have cleaned + * it up properly. + */ +static isc_result_t +postgres_get_resultset(const char *zone, const char *record, + const char *client, unsigned int query, + void *dbdata, PGresult **rs) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + char *querystring = NULL; + unsigned int i = 0; + unsigned int j = 0; + + /* temporarily get a unique thread # */ + unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0)); + + REQUIRE(*rs == NULL); + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d Getting DBI", dlz_thread_num); + + /* get db instance / connection */ +#ifdef ISC_PLATFORM_USETHREADS + + /* find an available DBI from the list */ + dbi = postgres_find_avail_conn((db_list_t *) dbdata); + +#else /* ISC_PLATFORM_USETHREADS */ + + /* + * only 1 DBI - no need to lock instance lock either + * only 1 thread in the whole process, no possible contention. + */ + dbi = (dbinstance_t *) dbdata; + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d Got DBI - checking query", dlz_thread_num); + + /* if DBI is null, can't do anything else */ + if (dbi == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + /* what type of query are we going to run? */ + switch(query) { + case ALLNODES: + /* + * if the query was not passed in from the config file + * then we can't run it. return not_implemented, so + * it's like the code for that operation was never + * built into the driver.... AHHH flexibility!!! + */ + if (dbi->allnodes_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case ALLOWXFR: + /* same as comments as ALLNODES */ + if (dbi->allowxfr_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case AUTHORITY: + /* same as comments as ALLNODES */ + if (dbi->authority_q == NULL) { + result = ISC_R_NOTIMPLEMENTED; + goto cleanup; + } + break; + case FINDZONE: + /* this is required. It's the whole point of DLZ! */ + if (dbi->findzone_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for findzone. " + "Findzone requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + case LOOKUP: + /* this is required. It's also a major point of DLZ! */ + if (dbi->lookup_q == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "No query specified for lookup. " + "Lookup requires a query"); + result = ISC_R_FAILURE; + goto cleanup; + } + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "postgres_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d checked query", dlz_thread_num); + + /* + * was a zone string passed? If so, make it safe for use in + * queries. + */ + if (zone != NULL) { + dbi->zone = postgres_escape_string(zone); + if (dbi->zone == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->zone = NULL; + } + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d did zone", dlz_thread_num); + + /* + * was a record string passed? If so, make it safe for use in + * queries. + */ + if (record != NULL) { + dbi->record = postgres_escape_string(record); + if (dbi->record == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->record = NULL; + } + + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d did record", dlz_thread_num); + + /* + * was a client string passed? If so, make it safe for use in + * queries. + */ + if (client != NULL) { + dbi->client = postgres_escape_string(client); + if (dbi->client == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + } else { /* no string passed, set the string pointer to NULL */ + dbi->client = NULL; + } + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d did client", dlz_thread_num); + + /* + * what type of query are we going to run? + * this time we build the actual query to run. + */ + switch(query) { + case ALLNODES: + querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); + break; + case ALLOWXFR: + querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); + break; + case AUTHORITY: + querystring = build_querystring(ns_g_mctx, dbi->authority_q); + break; + case FINDZONE: + querystring = build_querystring(ns_g_mctx, dbi->findzone_q); + break; + case LOOKUP: + querystring = build_querystring(ns_g_mctx, dbi->lookup_q); + break; + default: + /* + * this should never happen. If it does, the code is + * screwed up! + */ + UNEXPECTED_ERROR(__FILE__, __LINE__, + "Incorrect query flag passed to " + "postgres_get_resultset"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d built query", dlz_thread_num); + + /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ + if (querystring == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d query is '%s'", dlz_thread_num, querystring); + + /* + * output the full query string during debug so we can see + * what lame error the query has. + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "\nQuery String: %s\n", querystring); + + /* attempt query up to 3 times. */ + for (j=0; j < 3; j++) { + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d executing query for %d time", + dlz_thread_num, j); + /* try to get result set */ + *rs = PQexec((PGconn *)dbi->dbconn, querystring ); + result = ISC_R_SUCCESS; + /* + * if result set is null, reset DB connection, max 3 + * attempts. + */ + for (i=0; *rs == NULL && i < 3; i++) { + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d resetting connection", + dlz_thread_num); + result = ISC_R_FAILURE; + PQreset((PGconn *) dbi->dbconn); + /* connection ok, break inner loop */ + if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK) + break; + } + /* result set ok, break outter loop */ + if (PQresultStatus(*rs) == PGRES_TUPLES_OK) { + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d rs ok", dlz_thread_num); + break; + } else { + /* we got a result set object, but it's not right. */ + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d clearing rs", dlz_thread_num); + PQclear(*rs); /* get rid of it */ + /* in case this was the last attempt */ + result = ISC_R_FAILURE; + } + } + + cleanup: + /* it's always good to cleanup after yourself */ + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d cleaning up", dlz_thread_num); + + /* if we couldn't even allocate DBI, just return NULL */ + if (dbi == NULL) + return ISC_R_FAILURE; + + /* free dbi->zone string */ + if (dbi->zone != NULL) + isc_mem_free(ns_g_mctx, dbi->zone); + + /* free dbi->record string */ + if (dbi->record != NULL) + isc_mem_free(ns_g_mctx, dbi->record); + + /* free dbi->client string */ + if (dbi->client != NULL) + isc_mem_free(ns_g_mctx, dbi->client); + +#ifdef ISC_PLATFORM_USETHREADS + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d unlocking mutex", dlz_thread_num); + + /* release the lock so another thread can use this dbi */ + isc_mutex_unlock(&dbi->instance_lock); + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* release query string */ + if (querystring != NULL) + isc_mem_free(ns_g_mctx, querystring ); + + /* temporary logging message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "%d returning", dlz_thread_num); + + /* return result */ + return result; +} + +/*% + * The processing of result sets for lookup and authority are + * exactly the same. So that functionality has been moved + * into this function to minimize code. + */ + +static isc_result_t +postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs) +{ + isc_result_t result; + unsigned int i; + unsigned int rows; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + rows = PQntuples(rs); /* how many rows in result set */ + fields = PQnfields(rs); /* how many columns in result set */ + for (i=0; i < rows; i++) { + switch(fields) { + case 1: + /* + * one column in rs, it's the data field. use + * default type of A record, and default TTL + * of 86400 + */ + result = dns_sdlz_putrr(lookup, "a", 86400, + PQgetvalue(rs, i, 0)); + break; + case 2: + /* two columns, data field, and data type. + * use default TTL of 86400. + */ + result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0), + 86400, PQgetvalue(rs, i, 1)); + break; + case 3: + /* three columns, all data no defaults. + * convert text to int, make sure it worked + * right. + */ + ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver ttl must be " + "a positive number"); + } + result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1), + ttl, PQgetvalue(rs, i, 2)); + break; + default: + /* + * more than 3 fields, concatenate the last + * ones together. figure out how long to make + * string + */ + for (j=2, len=0; j < fields; j++) { + len += strlen(PQgetvalue(rs, i, j)) + 1; + } + /* + * allocate string memory, allow for NULL to + * term string + */ + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if (tmpString == NULL) { + /* major bummer, need more ram */ + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to " + "allocate memory for " + "temporary string"); + PQclear(rs); + return (ISC_R_FAILURE); /* Yeah, I'd say! */ + } + /* copy field to tmpString */ + strcpy(tmpString, PQgetvalue(rs, i, 2)); + /* + * concat the rest of fields together, space + * between each one. + */ + for (j=3; j < fields; j++) { + strcat(tmpString, " "); + strcat(tmpString, PQgetvalue(rs, i, j)); + } + /* convert text to int, make sure it worked right */ + ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver ttl must be " + "a postive number"); + } + /* ok, now tell Bind about it. */ + result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1), + ttl, tmpString); + /* done, get rid of this thing. */ + isc_mem_free(ns_g_mctx, tmpString); + } + /* I sure hope we were successful */ + if (result != ISC_R_SUCCESS) { + /* nope, get rid of the Result set, and log a msg */ + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putrr returned error. " + "Error code was: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + } + + /* free result set memory */ + PQclear(rs); + + /* if we did return results, we are successful */ + if (rows > 0) + return (ISC_R_SUCCESS); + + /* empty result set, no data found */ + return (ISC_R_NOTFOUND); +} + +/* + * SDLZ interface methods + */ + +/*% determine if the zone is supported by (in) the database */ + +static isc_result_t +postgres_findzone(void *driverarg, void *dbdata, const char *name) +{ + isc_result_t result; + PGresult *rs = NULL; + unsigned int rows; + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = postgres_get_resultset(name, NULL, NULL, + FINDZONE, dbdata, &rs); + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return " + "result set for findzone query"); + return (ISC_R_FAILURE); + } + /* count how many rows in result set */ + rows = PQntuples(rs); + /* get rid of result set, we are done with it. */ + PQclear(rs); + + /* if we returned any rows, zone is supported. */ + if (rows > 0) + return (ISC_R_SUCCESS); + + /* no rows returned, zone is not supported. */ + return (ISC_R_NOTFOUND); +} + +/*% Determine if the client is allowed to perform a zone transfer */ +static isc_result_t +postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + isc_result_t result; + PGresult *rs = NULL; + unsigned int rows; + UNUSED(driverarg); + + /* first check if the zone is supported by the database. */ + result = postgres_findzone(driverarg, dbdata, name); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOTFOUND); + + /* + * if we get to this point we know the zone is supported by + * the database the only questions now are is the zone + * transfer is allowed for this client and did the config file + * have an allow zone xfr query. + * + * Run our query, and get a result set from the database. + */ + result = postgres_get_resultset(name, NULL, client, + ALLOWXFR, dbdata, &rs); + /* if we get "not implemented", send it along. */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return " + "result set for allow xfr query"); + return (ISC_R_FAILURE); + } + /* count how many rows in result set */ + rows = PQntuples(rs); + /* get rid of result set, we are done with it. */ + PQclear(rs); + + /* if we returned any rows, zone xfr is allowed. */ + if (rows > 0) + return (ISC_R_SUCCESS); + + /* no rows returned, zone xfr not allowed */ + return (ISC_R_NOPERM); +} + +/*% + * If the client is allowed to perform a zone transfer, the next order of + * business is to get all the nodes in the zone, so bind can respond to the + * query. + */ +static isc_result_t +postgres_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + isc_result_t result; + PGresult *rs = NULL; + unsigned int i; + unsigned int rows; + unsigned int fields; + unsigned int j; + unsigned int len; + char *tmpString; + char *endp; + int ttl; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = postgres_get_resultset(zone, NULL, NULL, + ALLNODES, dbdata, &rs); + /* if we get "not implemented", send it along */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return " + "result set for all nodes query"); + return (ISC_R_FAILURE); + } + + rows = PQntuples(rs); /* how many rows in result set */ + fields = PQnfields(rs); /* how many columns in result set */ + for (i=0; i < rows; i++) { + if (fields < 4) { /* gotta have at least 4 columns */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver too few fields " + "returned by all nodes query"); + } + /* convert text to int, make sure it worked right */ + ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); + if (*endp != '\0' || ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver ttl must be " + "a postive number"); + } + if (fields == 4) { + /* tell Bind about it. */ + result = dns_sdlz_putnamedrr(allnodes, + PQgetvalue(rs, i, 2), + PQgetvalue(rs, i, 1), + ttl, + PQgetvalue(rs, i, 3)); + } else { + /* + * more than 4 fields, concatonat the last + * ones together. figure out how long to make + * string + */ + for (j=3, len=0; j < fields; j++) { + len += strlen(PQgetvalue(rs, i, j)) + 1; + } + /* allocate memory, allow for NULL to term string */ + tmpString = isc_mem_allocate(ns_g_mctx, len + 1); + if (tmpString == NULL) { /* we need more ram. */ + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to " + "allocate memory for " + "temporary string"); + PQclear(rs); + return (ISC_R_FAILURE); + } + /* copy this field to tmpString */ + strcpy(tmpString, PQgetvalue(rs, i, 3)); + /* concatonate the rest, with spaces between */ + for (j=4; j < fields; j++) { + strcat(tmpString, " "); + strcat(tmpString, PQgetvalue(rs, i, j)); + } + /* tell Bind about it. */ + result = dns_sdlz_putnamedrr(allnodes, + PQgetvalue(rs, i, 2), + PQgetvalue(rs, i, 1), + ttl, tmpString); + isc_mem_free(ns_g_mctx, tmpString); + } + /* if we weren't successful, log err msg */ + if (result != ISC_R_SUCCESS) { + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "dns_sdlz_putnamedrr returned error. " + "Error code was: %s", + isc_result_totext(result)); + return (ISC_R_FAILURE); + } + } + + /* free result set memory */ + PQclear(rs); + + /* if we did return results, we are successful */ + if (rows > 0) + return (ISC_R_SUCCESS); + + /* empty result set, no data found */ + return (ISC_R_NOTFOUND); +} + +/*% + * if the lookup function does not return SOA or NS records for the zone, + * use this function to get that information for Bind. + */ + +static isc_result_t +postgres_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + PGresult *rs = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = postgres_get_resultset(zone, NULL, NULL, + AUTHORITY, dbdata, &rs); + /* if we get "not implemented", send it along */ + if (result == ISC_R_NOTIMPLEMENTED) + return result; + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to return " + "result set for authority query"); + return (ISC_R_FAILURE); + } + /* + * lookup and authority result sets are processed in the same + * manner postgres_process_rs does the job for both + * functions. + */ + return postgres_process_rs(lookup, rs); +} + +/*% if zone is supported, lookup up a (or multiple) record(s) in it */ +static isc_result_t +postgres_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + PGresult *rs = NULL; + + UNUSED(driverarg); + + /* run the query and get the result set from the database. */ + result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs); + /* if we didn't get a result set, log an err msg. */ + if (result != ISC_R_SUCCESS) { + if (rs != NULL) + PQclear(rs); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver unable to " + "return result set for lookup query"); + return (ISC_R_FAILURE); + } + /* + * lookup and authority result sets are processed in the same + * manner postgres_process_rs does the job for both functions. + */ + return postgres_process_rs(lookup, rs); +} + +/*% + * create an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement which is + * passed into all query functions. + */ +static isc_result_t +postgres_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + isc_result_t result; + dbinstance_t *dbi = NULL; + unsigned int j; + +#ifdef ISC_PLATFORM_USETHREADS + /* if multi-threaded, we need a few extra variables. */ + int dbcount; + db_list_t *dblist = NULL; + int i; + char *endp; + +#endif /* ISC_PLATFORM_USETHREADS */ + + UNUSED(driverarg); + UNUSED(dlzname); + +/* seed random # generator */ + srand( (unsigned)time( NULL ) ); + + +#ifdef ISC_PLATFORM_USETHREADS + /* if debugging, let user know we are multithreaded. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Postgres driver running multithreaded"); +#else /* ISC_PLATFORM_USETHREADS */ + /* if debugging, let user know we are single threaded. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), + "Postgres driver running single threaded"); +#endif /* ISC_PLATFORM_USETHREADS */ + + /* verify we have at least 5 arg's passed to the driver */ + if (argc < 5) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver requires at least " + "4 command line args."); + return (ISC_R_FAILURE); + } + + /* no more than 8 arg's should be passed to the driver */ + if (argc > 8) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver cannot accept more than " + "7 command line args."); + return (ISC_R_FAILURE); + } + + /* multithreaded build can have multiple DB connections */ +#ifdef ISC_PLATFORM_USETHREADS + + /* check how many db connections we should create */ + dbcount = strtol(argv[1], &endp, 10); + if (*endp != '\0' || dbcount < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver database connection count " + "must be positive."); + return (ISC_R_FAILURE); + } + + /* allocate memory for database connection list */ + dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); + if (dblist == NULL) + return (ISC_R_NOMEMORY); + + /* initialize DB connection list */ + ISC_LIST_INIT(*dblist); + + /* + * create the appropriate number of database instances (DBI) + * append each new DBI to the end of the list + */ + for (i=0; i < dbcount; i++) { + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* how many queries were passed in from config file? */ + switch(argc) { + case 5: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, + NULL, argv[3], argv[4], + NULL, &dbi); + break; + case 6: + result = build_sqldbinstance(ns_g_mctx, NULL, NULL, + argv[5], argv[3], argv[4], + NULL, &dbi); + break; + case 7: + result = build_sqldbinstance(ns_g_mctx, argv[6], NULL, + argv[5], argv[3], argv[4], + NULL, &dbi); + break; + case 8: + result = build_sqldbinstance(ns_g_mctx, argv[6], + argv[7], argv[5], argv[3], + argv[4], NULL, &dbi); + break; + default: + /* not really needed, should shut up compiler. */ + result = ISC_R_FAILURE; + } + + + if (result == ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Postgres driver created database " + "instance object."); + } else { /* unsuccessful?, log err msg and cleanup. */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver could not create " + "database instance object."); + goto cleanup; + } + +#ifdef ISC_PLATFORM_USETHREADS + + /* when multithreaded, build a list of DBI's */ + ISC_LINK_INIT(dbi, link); + ISC_LIST_APPEND(*dblist, dbi, link); + +#endif + + /* create and set db connection */ + dbi->dbconn = PQconnectdb(argv[2]); + /* + * if db connection cannot be created, log err msg and + * cleanup. + */ + if (dbi->dbconn == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver could not allocate " + "memory for database connection"); + goto cleanup; + } + + /* if we cannot connect the first time, try 3 more times. */ + for (j = 0; + PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK && + j < 3; + j++) + PQreset((PGconn *) dbi->dbconn); + + +#ifdef ISC_PLATFORM_USETHREADS + + /* + * if multi threaded, let user know which connection + * failed. user could be attempting to create 10 db + * connections and for some reason the db backend only + * allows 9 + */ + if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver failed to create " + "database connection number %u " + "after 4 attempts", + i + 1); + goto cleanup; + } + + /* set DBI = null for next loop through. */ + dbi = NULL; + } /* end for loop */ + + /* set dbdata to the list we created. */ + *dbdata = dblist; + +#else /* ISC_PLATFORM_USETHREADS */ + /* if single threaded, just let user know we couldn't connect. */ + if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Postgres driver failed to create database " + "connection after 4 attempts"); + goto cleanup; + } + + /* + * single threaded build can only use 1 db connection, return + * it via dbdata + */ + *dbdata = dbi; + +#endif /* ISC_PLATFORM_USETHREADS */ + + /* hey, we got through all of that ok, return success. */ + return(ISC_R_SUCCESS); + + cleanup: + +#ifdef ISC_PLATFORM_USETHREADS + /* + * if multithreaded, we could fail because only 1 connection + * couldn't be made. We should cleanup the other successful + * connections properly. + */ + postgres_destroy_dblist(dblist); + +#else /* ISC_PLATFORM_USETHREADS */ + if (dbi != NULL) + destroy_sqldbinstance(dbi); + +#endif /* ISC_PLATFORM_USETHREADS */ + return(ISC_R_FAILURE); +} + +/*% + * destroy an instance of the driver. Remember, only 1 copy of the driver's + * code is ever loaded, the driver has to remember which context it's + * operating in. This is done via use of the dbdata arguement. + * so we really only need to clean it up since we are not using driverarg. + */ +static void +postgres_destroy(void *driverarg, void *dbdata) +{ + +#ifdef ISC_PLATFORM_USETHREADS + + UNUSED(driverarg); + /* cleanup the list of DBI's */ + postgres_destroy_dblist((db_list_t *) dbdata); + +#else /* ISC_PLATFORM_USETHREADS */ + + dbinstance_t *dbi; + + UNUSED(driverarg); + + dbi = (dbinstance_t *) dbdata; + + /* release DB connection */ + if (dbi->dbconn != NULL) + PQfinish((PGconn *) dbi->dbconn); + + /* destroy single DB instance */ + destroy_sqldbinstance(dbi); + +#endif /* ISC_PLATFORM_USETHREADS */ +} + +/* pointers to all our runtime methods. */ +/* this is used during driver registration */ +/* i.e. in dlz_postgres_init below. */ +static dns_sdlzmethods_t dlz_postgres_methods = { + postgres_create, + postgres_destroy, + postgres_findzone, + postgres_lookup, + postgres_authority, + postgres_allnodes, + postgres_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_postgres_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ postgres driver."); + + /* + * Driver is always threadsafe. When multithreaded all + * functions use multithreaded code. When not multithreaded, + * all functions can only be entered once, but only 1 thread + * of operation is available in Bind. So everything is still + * threadsafe. + */ + result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + ns_g_mctx, &dlz_postgres); + /* if we can't register the driver, there are big problems. */ + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_postgres_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ postgres driver."); + + /* unregister the driver. */ + if (dlz_postgres != NULL) + dns_sdlzunregister(&dlz_postgres); +} + +#endif diff --git a/contrib/dlz/drivers/dlz_stub_driver.c b/contrib/dlz/drivers/dlz_stub_driver.c new file mode 100644 index 00000000000..f9a3bb8f0a9 --- /dev/null +++ b/contrib/dlz/drivers/dlz_stub_driver.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ_STUB + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static dns_sdlzimplementation_t *dlz_stub = NULL; + +typedef struct config_data { + char *myzone; + char *myname; + char *myip; + isc_mem_t *mctx; +} config_data_t; + +/* + * SDLZ methods + */ + +static isc_result_t +stub_dlz_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) +{ + config_data_t *cd; + isc_result_t result; + + UNUSED(zone); + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + result = dns_sdlz_putnamedrr(allnodes, cd->myname, "soa", 86400, + "web root.localhost. " + "0 28800 7200 604800 86400"); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + result = dns_sdlz_putnamedrr(allnodes, "ns", "ns", 86400, cd->myname); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + result = dns_sdlz_putnamedrr(allnodes, cd->myname, "a", 1, cd->myip); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + return (ISC_R_SUCCESS); +} + +static isc_result_t +stub_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) +{ + UNUSED(driverarg); + UNUSED(dbdata); + UNUSED(name); + UNUSED(client); + return ISC_R_SUCCESS; +} + +static isc_result_t +stub_dlz_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + config_data_t *cd; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + if (strcmp(zone, cd->myzone) == 0) { + result = dns_sdlz_putsoa(lookup, cd->myname, + "root.localhost.", 0); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + result = dns_sdlz_putrr(lookup, "ns", 86400, cd->myname); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + return (ISC_R_SUCCESS); + } + return (ISC_R_NOTFOUND); +} + +static isc_result_t +stub_dlz_findzonedb(void *driverarg, void *dbdata, const char *name) +{ + + config_data_t *cd; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + /* Write info message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "dlz_stub findzone looking for '%s'", name); + + if (strcmp(cd->myzone, name) == 0) + return (ISC_R_SUCCESS); + else + return (ISC_R_SUCCESS); +} + + +static isc_result_t +stub_dlz_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup) +{ + isc_result_t result; + config_data_t *cd; + + UNUSED(zone); + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + if (strcmp(name, cd->myname) == 0) { + result = dns_sdlz_putrr(lookup, "a", 1, cd->myip); + if (result != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + return (ISC_R_SUCCESS); + } + return (ISC_R_FAILURE); + +} + + +static isc_result_t +stub_dlz_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) +{ + + config_data_t *cd; + + UNUSED(driverarg); + + if (argc < 4) + return (ISC_R_FAILURE); + /* + * Write info message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Loading '%s' using DLZ_stub driver. " + "Zone: %s, Name: %s IP: %s", + dlzname, argv[1], argv[2], argv[3]); + + cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t)); + if ((cd) == NULL) { + return (ISC_R_NOMEMORY); + } + + memset(cd, 0, sizeof(config_data_t)); + + cd->myzone = isc_mem_strdup(ns_g_mctx, argv[1]); + if (cd->myzone == NULL) { + isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t)); + return (ISC_R_NOMEMORY); + } + + cd->myname = isc_mem_strdup(ns_g_mctx, argv[2]); + if (cd->myname == NULL) { + isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t)); + isc_mem_free(ns_g_mctx, cd->myzone); + return (ISC_R_NOMEMORY); + } + + cd->myip = isc_mem_strdup(ns_g_mctx, argv[3]); + if (cd->myip == NULL) { + isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t)); + isc_mem_free(ns_g_mctx, cd->myname); + isc_mem_free(ns_g_mctx, cd->myzone); + return (ISC_R_NOMEMORY); + } + + isc_mem_attach(ns_g_mctx, &cd->mctx); + + *dbdata = cd; + + return(ISC_R_SUCCESS); +} + +static void +stub_dlz_destroy(void *driverarg, void *dbdata) +{ + config_data_t *cd; + isc_mem_t *mctx; + + UNUSED(driverarg); + + cd = (config_data_t *) dbdata; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unloading DLZ_stub driver."); + + isc_mem_free(ns_g_mctx, cd->myzone); + isc_mem_free(ns_g_mctx, cd->myname); + isc_mem_free(ns_g_mctx, cd->myip); + mctx = cd->mctx; + isc_mem_put(mctx, cd, sizeof(config_data_t)); + isc_mem_detach(&mctx); +} + +static dns_sdlzmethods_t dlz_stub_methods = { + stub_dlz_create, + stub_dlz_destroy, + stub_dlz_findzonedb, + stub_dlz_lookup, + stub_dlz_authority, + stub_dlz_allnodes, + stub_dlz_allowzonexfr +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_stub_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ_stub driver."); + + result = dns_sdlzregister("dlz_stub", &dlz_stub_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA, + ns_g_mctx, &dlz_stub); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + + return result; +} + +/* + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_stub_clear(void) { + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ_stub driver."); + + if (dlz_stub != NULL) + dns_sdlzunregister(&dlz_stub); +} + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_bdb_driver.h b/contrib/dlz/drivers/include/dlz/dlz_bdb_driver.h new file mode 100644 index 00000000000..1d706fc8b99 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_bdb_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_BDB_DRIVER_H +#define DLZ_BDB_DRIVER_H + +isc_result_t +dlz_bdb_init(void); + +void +dlz_bdb_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_bdbhpt_driver.h b/contrib/dlz/drivers/include/dlz/dlz_bdbhpt_driver.h new file mode 100644 index 00000000000..0ccbc8da809 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_bdbhpt_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_BDBHPT_DRIVER_H +#define DLZ_BDBHPT_DRIVER_H + +isc_result_t +dlz_bdbhpt_init(void); + +void +dlz_bdbhpt_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_drivers.h b/contrib/dlz/drivers/include/dlz/dlz_drivers.h new file mode 100644 index 00000000000..dce7cd20e2f --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_drivers.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC") + * + * 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: dlz_drivers.h,v 1.2 2005/09/05 00:10:58 marka Exp $ */ + +#ifndef DLZ_DRIVERS_H +#define DLZ_DRIVERS_H 1 + +/*! \file */ + +isc_result_t +dlz_drivers_init(void); + +void +dlz_drivers_clear(void); + +#endif /* DLZ_DRIVERS_H */ diff --git a/contrib/dlz/drivers/include/dlz/dlz_filesystem_driver.h b/contrib/dlz/drivers/include/dlz/dlz_filesystem_driver.h new file mode 100644 index 00000000000..627427d941b --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_filesystem_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_FILESYSTEM_DRIVER_H +#define DLZ_FILESYSTEM_DRIVER_H + +isc_result_t +dlz_fs_init(void); + +void +dlz_fs_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_ldap_driver.h b/contrib/dlz/drivers/include/dlz/dlz_ldap_driver.h new file mode 100644 index 00000000000..51efbf497f0 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_ldap_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_LDAP_DRIVER_H +#define DLZ_LDAP_DRIVER_H + +isc_result_t +dlz_ldap_init(void); + +void +dlz_ldap_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_mysql_driver.h b/contrib/dlz/drivers/include/dlz/dlz_mysql_driver.h new file mode 100644 index 00000000000..b742838bc81 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_mysql_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_MYSQL_DRIVER_H +#define DLZ_MYSQL_DRIVER_H + +isc_result_t +dlz_mysql_init(void); + +void +dlz_mysql_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_odbc_driver.h b/contrib/dlz/drivers/include/dlz/dlz_odbc_driver.h new file mode 100644 index 00000000000..5a072d8c987 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_odbc_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_ODBC_DRIVER_H +#define DLZ_ODBC_DRIVER_H + +isc_result_t +dlz_odbc_init(void); + +void +dlz_odbc_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_postgres_driver.h b/contrib/dlz/drivers/include/dlz/dlz_postgres_driver.h new file mode 100644 index 00000000000..3416dceadf0 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_postgres_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_POSTGRES_DRIVER_H +#define DLZ_POSTGRES_DRIVER_H + +isc_result_t +dlz_postgres_init(void); + +void +dlz_postgres_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/dlz_stub_driver.h b/contrib/dlz/drivers/include/dlz/dlz_stub_driver.h new file mode 100644 index 00000000000..a517c69adc9 --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/dlz_stub_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 DLZ_STUB_DRIVER_H +#define DLZ_STUB_DRIVER_H + +isc_result_t +dlz_stub_init(void); + +void +dlz_stub_clear(void); + +#endif diff --git a/contrib/dlz/drivers/include/dlz/sdlz_helper.h b/contrib/dlz/drivers/include/dlz/sdlz_helper.h new file mode 100644 index 00000000000..764a05af7cf --- /dev/null +++ b/contrib/dlz/drivers/include/dlz/sdlz_helper.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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 SDLZHELPER_H +#define SDLZHELPER_H + +/* + * Types + */ +#define SDLZH_REQUIRE_CLIENT 0x01 +#define SDLZH_REQUIRE_QUERY 0x02 +#define SDLZH_REQUIRE_RECORD 0x04 +#define SDLZH_REQUIRE_ZONE 0x08 + +typedef struct query_segment query_segment_t; +typedef ISC_LIST(query_segment_t) query_list_t; +typedef struct dbinstance dbinstance_t; +typedef ISC_LIST(dbinstance_t) db_list_t; +typedef struct driverinstance driverinstance_t; + +/*% + * a query segment is all the text between our special tokens + * special tokens are %zone%, %record%, %client% + */ +struct query_segment { + void *sql; + unsigned int strlen; + isc_boolean_t direct; + ISC_LINK(query_segment_t) link; +}; + +/*% + * a database instance contains everything we need for running + * a query against the database. Using it each separate thread + * can dynamically construct a query and execute it against the + * database. The "instance_lock" and locking code in the driver's + * make sure no two threads try to use the same DBI at a time. + */ +struct dbinstance { + void *dbconn; + query_list_t *allnodes_q; + query_list_t *allowxfr_q; + query_list_t *authority_q; + query_list_t *findzone_q; + query_list_t *lookup_q; + query_list_t *countzone_q; + char *query_buf; + char *zone; + char *record; + char *client; + isc_mem_t *mctx; + isc_mutex_t instance_lock; + ISC_LINK(dbinstance_t) link; +}; + +/* + * Method declarations + */ + +/* see the code in sdlz_helper.c for more information on these methods */ + +char * +sdlzh_build_querystring(isc_mem_t *mctx, query_list_t *querylist); + +isc_result_t +sdlzh_build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, + const char *allowxfr_str, const char *authority_str, + const char *findzone_str, const char *lookup_str, + const char *countzone_str, dbinstance_t **dbi); + +void +sdlzh_destroy_sqldbinstance(dbinstance_t *dbi); + +char * +sdlzh_get_parameter_value(isc_mem_t *mctx, const char *input, const char* key); + +/* Compatability with existing DLZ drivers */ + +#define build_querystring sdlzh_build_querystring +#define build_sqldbinstance sdlzh_build_sqldbinstance +#define destroy_sqldbinstance sdlzh_destroy_sqldbinstance + +#define getParameterValue(x,y) sdlzh_get_parameter_value(ns_g_mctx, (x), (y)) + +#endif /* SDLZHELPER_H */ diff --git a/contrib/dlz/drivers/rules.in b/contrib/dlz/drivers/rules.in new file mode 100644 index 00000000000..9caabcb431c --- /dev/null +++ b/contrib/dlz/drivers/rules.in @@ -0,0 +1,47 @@ +# Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC 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. + +# $Id: rules.in,v 1.2 2005/09/05 00:10:57 marka Exp $ + +dlz_drivers.@O@: ${DLZ_DRIVER_DIR}/dlz_drivers.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_drivers.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_drivers.c + +sdlz_helper.@O@: ${DLZ_DRIVER_DIR}/sdlz_helper.c ${DLZ_DRIVER_DIR}/include/dlz/sdlz_helper.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/sdlz_helper.c + + +dlz_bdb_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_bdb_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_bdb_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_bdb_driver.c + +dlz_bdbhpt_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_bdbhpt_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_bdbhpt_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_bdbhpt_driver.c + +dlz_filesystem_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_filesystem_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_filesystem_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_filesystem_driver.c + +dlz_ldap_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_ldap_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_ldap_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_ldap_driver.c + +dlz_mysql_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_mysql_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_mysql_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_mysql_driver.c + +dlz_odbc_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_odbc_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_odbc_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_odbc_driver.c + +dlz_postgres_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_postgres_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_postgres_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_postgres_driver.c + +dlz_stub_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_stub_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_stub_driver.h + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_stub_driver.c + diff --git a/contrib/dlz/drivers/sdlz_helper.c b/contrib/dlz/drivers/sdlz_helper.c new file mode 100644 index 00000000000..ea33db1b916 --- /dev/null +++ b/contrib/dlz/drivers/sdlz_helper.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef DLZ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +/* + * sdlz helper methods + */ + +/*% + * properly destroys a querylist by de-allocating the + * memory for each query segment, and then the list itself + */ + +static void +destroy_querylist(isc_mem_t *mctx, query_list_t **querylist) +{ + query_segment_t *tseg = NULL; + query_segment_t *nseg = NULL; + + REQUIRE(mctx != NULL); + + /* if query list is null, nothing to do */ + if (*querylist == NULL) + return; + + /* start at the top of the list */ + nseg = ISC_LIST_HEAD(**querylist); + while (nseg != NULL) { /* loop, until end of list */ + tseg = nseg; + /* + * free the query segment's text string but only if it + * was really a query segment, and not a pointer to + * %zone%, or %record%, or %client% + */ + if (tseg->sql != NULL && tseg->direct == isc_boolean_true) + isc_mem_free(mctx, tseg->sql); + /* get the next query segment, before we destroy this one. */ + nseg = ISC_LIST_NEXT(nseg, link); + /* deallocate this query segment. */ + isc_mem_put(mctx, tseg, sizeof(query_segment_t)); + } + /* deallocate the query segment list */ + isc_mem_put(mctx, *querylist, sizeof(query_list_t)); +} + +/*% constructs a query list by parsing a string into query segments */ +static isc_result_t +build_querylist(isc_mem_t *mctx, const char *query_str, char **zone, + char **record, char **client, query_list_t **querylist, + unsigned int flags) +{ + isc_result_t result; + isc_boolean_t foundzone = isc_boolean_false; + isc_boolean_t foundrecord = isc_boolean_false; + isc_boolean_t foundclient = isc_boolean_false; + char *temp_str = NULL; + char *right_str = NULL; + query_list_t *tql; + query_segment_t *tseg = NULL; + + REQUIRE(querylist != NULL && *querylist == NULL); + REQUIRE(mctx != NULL); + + /* if query string is null, or zero length */ + if (query_str == NULL || strlen(query_str) < 1) { + if ((flags & SDLZH_REQUIRE_QUERY) == 0) + /* we don't need it were ok. */ + return (ISC_R_SUCCESS); + else + /* we did need it, PROBLEM!!! */ + return (ISC_R_FAILURE); + } + + /* allocate memory for query list */ + tql = isc_mem_get(mctx, sizeof(query_list_t)); + /* couldn't allocate memory. Problem!! */ + if (tql == NULL) + return (ISC_R_NOMEMORY); + + /* initialize the query segment list */ + ISC_LIST_INIT(*tql); + + /* make a copy of query_str so we can chop it up */ + temp_str = right_str = isc_mem_strdup(mctx, query_str); + /* couldn't make a copy, problem!! */ + if (right_str == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + /* loop through the string and chop it up */ + while (right_str != NULL) { + /* allocate memory for tseg */ + tseg = isc_mem_get(mctx, sizeof(query_segment_t)); + if (tseg == NULL) { /* no memory, clean everything up. */ + result = ISC_R_NOMEMORY; + goto cleanup; + } + tseg->sql = NULL; + tseg->direct = isc_boolean_false; + /* initialize the query segment link */ + ISC_LINK_INIT(tseg, link); + /* append the query segment to the list */ + ISC_LIST_APPEND(*tql, tseg, link); + + /* + * split string at the first "%". set query segment to + * left portion + */ + tseg->sql = isc_mem_strdup(mctx, + isc_string_separate(&right_str, + "%")); + if (tseg->sql == NULL) { + /* no memory, clean everything up. */ + result = ISC_R_NOMEMORY; + goto cleanup; + } + /* tseg->sql points directly to a string. */ + tseg->direct = isc_boolean_true; + tseg->strlen = strlen(tseg->sql); + + /* check if we encountered "%zone%" token */ + if (strcasecmp(tseg->sql, "zone") == 0) { + /* + * we don't really need, or want the "zone" + * text, so get rid of it. + */ + isc_mem_free(mctx, tseg->sql); + /* set tseg->sql to in-direct zone string */ + tseg->sql = (char**) zone; + tseg->strlen = 0; + /* tseg->sql points in-directly to a string */ + tseg->direct = isc_boolean_false; + foundzone = isc_boolean_true; + /* check if we encountered "%record%" token */ + } else if (strcasecmp(tseg->sql, "record") == 0) { + /* + * we don't really need, or want the "record" + * text, so get rid of it. + */ + isc_mem_free(mctx, tseg->sql); + /* set tseg->sql to in-direct record string */ + tseg->sql = (char**) record; + tseg->strlen = 0; + /* tseg->sql points in-directly poinsts to a string */ + tseg->direct = isc_boolean_false; + foundrecord = isc_boolean_true; + /* check if we encountered "%client%" token */ + } else if (strcasecmp(tseg->sql, "client") == 0) { + /* + * we don't really need, or want the "client" + * text, so get rid of it. + */ + isc_mem_free(mctx, tseg->sql); + /* set tseg->sql to in-direct record string */ + tseg->sql = (char**) client; + tseg->strlen = 0; + /* tseg->sql points in-directly poinsts to a string */ + tseg->direct = isc_boolean_false; + foundclient = isc_boolean_true; + } + } + + /* we don't need temp_str any more */ + isc_mem_free(mctx, temp_str); + /* + * add checks later to verify zone and record are found if + * necessary. + */ + + /* if this query requires %client%, make sure we found it */ + if (((flags & SDLZH_REQUIRE_CLIENT) != 0) && (!foundclient) ) { + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%client%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + /* if this query requires %record%, make sure we found it */ + if (((flags & SDLZH_REQUIRE_RECORD) != 0) && (!foundrecord) ) { + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%record%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + /* if this query requires %zone%, make sure we found it */ + if (((flags & SDLZH_REQUIRE_ZONE) != 0) && (!foundzone) ) { + /* Write error message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Required token %%zone%% not found."); + result = ISC_R_FAILURE; + goto flag_fail; + } + + /* pass back the query list */ + *querylist = (query_list_t *) tql; + + /* return success */ + return (ISC_R_SUCCESS); + + cleanup: + /* get rid of temp_str */ + if (temp_str != NULL) + isc_mem_free(mctx, temp_str); + + flag_fail: + /* get rid of what was build of the query list */ + if (tql != NULL) + destroy_querylist(mctx, &tql); + return result; +} + +/*% + * build a query string from query segments, and dynamic segments + * dynamic segments replace where the tokens %zone%, %record%, %client% + * used to be in our queries from named.conf + */ +char * +sdlzh_build_querystring(isc_mem_t *mctx, query_list_t *querylist) +{ + query_segment_t *tseg = NULL; + unsigned int length = 0; + char *qs = NULL; + + REQUIRE(mctx != NULL); + REQUIRE(querylist != NULL); + + /* start at the top of the list */ + tseg = ISC_LIST_HEAD(*querylist); + while (tseg != NULL) { + /* + * if this is a query segment, use the + * precalculated string length + */ + if (tseg->direct == isc_boolean_true) + length += tseg->strlen; + else /* calculate string length for dynamic segments. */ + length += strlen(* (char**) tseg->sql); + /* get the next segment */ + tseg = ISC_LIST_NEXT(tseg, link); + } + + /* allocate memory for the string */ + qs = isc_mem_allocate(mctx, length + 1); + /* couldn't allocate memory, We need more ram! */ + if (qs == NULL) + return NULL; + + /* start at the top of the list again */ + tseg = ISC_LIST_HEAD(*querylist); + /* copy the first item in the list to the query string */ + if (tseg->direct == isc_boolean_true) /* query segment */ + strcpy(qs, tseg->sql); + else + strcpy(qs, * (char**) tseg->sql); /* dynamic segment */ + + /* concatonate the rest of the segments */ + while ((tseg = ISC_LIST_NEXT(tseg, link)) != NULL) { + if (tseg->direct == isc_boolean_true) + /* query segments */ + strcat(qs, tseg->sql); + else + /* dynamic segments */ + strcat(qs, * (char**) tseg->sql); + } + + return qs; +} + +/*% constructs a sql dbinstance (DBI) */ +isc_result_t +sdlzh_build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, + const char *allowxfr_str, const char *authority_str, + const char *findzone_str, const char *lookup_str, + const char *countzone_str, dbinstance_t **dbi) +{ + + isc_result_t result; + dbinstance_t *db = NULL; + + REQUIRE(dbi != NULL && *dbi == NULL); + REQUIRE(mctx != NULL); + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(mctx, sizeof(dbinstance_t)); + if (db == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not allocate memory for " + "database instance object."); + return (ISC_R_NOMEMORY); + } + memset(db, 0, sizeof(dbinstance_t)); + db->dbconn = NULL; + db->client = NULL; + db->record = NULL; + db->zone = NULL; + db->mctx = NULL; + db->query_buf = NULL; + db->allnodes_q = NULL; + db->allowxfr_q = NULL; + db->authority_q = NULL; + db->findzone_q = NULL; + db->countzone_q = NULL; + db->lookup_q = NULL; + + /* attach to the memory context */ + isc_mem_attach(mctx, &db->mctx); + + /* initialize the reference count mutex */ + result = isc_mutex_init(&db->instance_lock); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + goto cleanup; + } + + /* build the all nodes query list */ + result = build_querylist(mctx, allnodes_str, &db->zone, + &db->record, &db->client, + &db->allnodes_q, SDLZH_REQUIRE_ZONE); + /* if unsuccessful, log err msg and cleanup */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build all nodes query list"); + goto cleanup; + } + + /* build the allow zone transfer query list */ + result = build_querylist(mctx, allowxfr_str, &db->zone, + &db->record, &db->client, + &db->allowxfr_q, + SDLZH_REQUIRE_ZONE | SDLZH_REQUIRE_CLIENT); + /* if unsuccessful, log err msg and cleanup */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build allow xfr query list"); + goto cleanup; + } + + /* build the authority query, query list */ + result = build_querylist(mctx, authority_str, &db->zone, + &db->record, &db->client, + &db->authority_q, SDLZH_REQUIRE_ZONE); + /* if unsuccessful, log err msg and cleanup */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build authority query list"); + goto cleanup; + } + + /* build findzone query, query list */ + result = build_querylist(mctx, findzone_str, &db->zone, + &db->record, &db->client, + &db->findzone_q, SDLZH_REQUIRE_ZONE); + /* if unsuccessful, log err msg and cleanup */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build find zone query list"); + goto cleanup; + } + + /* build countzone query, query list */ + result = build_querylist(mctx, countzone_str, &db->zone, + &db->record, &db->client, + &db->countzone_q, SDLZH_REQUIRE_ZONE); + /* if unsuccessful, log err msg and cleanup */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build count zone query list"); + goto cleanup; + } + + /* build lookup query, query list */ + result = build_querylist(mctx, lookup_str, &db->zone, + &db->record, &db->client, + &db->lookup_q, SDLZH_REQUIRE_RECORD); + /* if unsuccessful, log err msg and cleanup */ + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Could not build lookup query list"); + goto cleanup; + } + + /* pass back the db instance */ + *dbi = (dbinstance_t *) db; + + /* return success */ + return (ISC_R_SUCCESS); + + cleanup: + /* destroy whatever was build of the db instance */ + destroy_sqldbinstance(db); + /* return failure */ + return (ISC_R_FAILURE); +} + +void +sdlzh_destroy_sqldbinstance(dbinstance_t *dbi) +{ + isc_mem_t *mctx; + + /* save mctx for later */ + mctx = dbi->mctx; + + /* destroy any query lists we created */ + destroy_querylist(mctx, &dbi->allnodes_q); + destroy_querylist(mctx, &dbi->allowxfr_q); + destroy_querylist(mctx, &dbi->authority_q); + destroy_querylist(mctx, &dbi->findzone_q); + destroy_querylist(mctx, &dbi->countzone_q); + destroy_querylist(mctx, &dbi->lookup_q); + + /* get rid of the mutex */ + isc_mutex_destroy(&dbi->instance_lock); + + /* return, and detach the memory */ + isc_mem_put(mctx, dbi, sizeof(dbinstance_t)); + isc_mem_detach(&mctx); +} + +char * +sdlzh_get_parameter_value(isc_mem_t *mctx, const char *input, const char* key) +{ + int keylen; + char *keystart; + char value[255]; + int i; + + if (key == NULL || input == NULL || strlen(input) < 1) + return NULL; + + keylen = strlen(key); + + if (keylen < 1) + return NULL; + + keystart = strstr(input, key); + + if (keystart == NULL) + return NULL; + + REQUIRE(mctx != NULL); + + for (i = 0; i < 255; i++) { + value[i] = keystart[keylen + i]; + if (value[i] == ' ' || value[i] == '\0') { + value[i] = '\0'; + break; + } + } + + return isc_mem_strdup(mctx, value); +} + +#endif diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index ed7c667436c..e35ee77962e 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.151 2005/07/18 23:59:36 marka Exp $ +# $Id: Makefile.in,v 1.152 2005/09/05 00:11:01 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -52,14 +52,15 @@ DSTOBJS = dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ compress.@O@ \ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \ - dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \ + dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \ lib.@O@ log.@O@ lookup.@O@ \ master.@O@ masterdump.@O@ message.@O@ \ name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \ rdatalist.@O@ \ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \ - resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ soa.@O@ ssu.@O@ \ + resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ sdlz.@O@ \ + soa.@O@ ssu.@O@ \ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \ tsig.@O@ ttl.@O@ validator.@O@ \ version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@ @@ -76,14 +77,15 @@ DSTSRCS = dst_api.c dst_lib.c dst_parse.c \ DNSSRCS = acache.c acl.c adb.c byaddr.c \ cache.c callbacks.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ - dnssec.c ds.c forward.c journal.c keytable.c \ + dlz.c dnssec.c ds.c forward.c journal.c keytable.c \ lib.c log.c lookup.c \ master.c masterdump.c message.c \ name.c ncache.c nsec.c order.c peer.c portlist.c \ rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \ rdatalist.c \ rdataset.c rdatasetiter.c rdataslab.c request.c \ - resolver.c result.c rootns.c sdb.c soa.c ssu.c \ + resolver.c result.c rootns.c sdb.c sdlz.c \ + soa.c ssu.c \ stats.c tcpmsg.c time.c timer.c tkey.c \ tsig.c ttl.c validator.c \ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS} diff --git a/lib/dns/dlz.c b/lib/dns/dlz.c new file mode 100644 index 00000000000..ffd243686b2 --- /dev/null +++ b/lib/dns/dlz.c @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: dlz.c,v 1.2 2005/09/05 00:11:01 marka Exp $ */ + +/*! \file */ + +/*** + *** Imports + ***/ + +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include + +/*** + *** Supported DLZ DB Implementations Registry + ***/ + +static ISC_LIST(dns_dlzimplementation_t) dlz_implementations; +static isc_rwlock_t dlz_implock; +static isc_once_t once = ISC_ONCE_INIT; + +static void +dlz_initialize(void) { + RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS); + ISC_LIST_INIT(dlz_implementations); +} + +/*% + * Searches the dlz_implementations list for a driver matching name. + */ +static inline dns_dlzimplementation_t * +dlz_impfind(const char *name) { + dns_dlzimplementation_t *imp; + + for (imp = ISC_LIST_HEAD(dlz_implementations); + imp != NULL; + imp = ISC_LIST_NEXT(imp, link)) + if (strcasecmp(name, imp->name) == 0) + return (imp); + return (NULL); +} + +/*** + *** Basic DLZ Methods + ***/ + +isc_result_t +dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name, + isc_sockaddr_t *clientaddr, dns_db_t **dbp) +{ + isc_result_t result; + dns_dlzallowzonexfr_t allowzonexfr; + dns_dlzdb_t *dlzdatabase; + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(DNS_DLZ_VALID(view->dlzdatabase)); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + /* ask driver if the zone is supported */ + dlzdatabase = view->dlzdatabase; + allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr; + result = (*allowzonexfr)(dlzdatabase->implementation->driverarg, + dlzdatabase->dbdata, dlzdatabase->mctx, + view->rdclass, name, clientaddr, dbp); + + if (result == ISC_R_NOTIMPLEMENTED) + return (ISC_R_NOTFOUND); + return (result); +} + +isc_result_t +dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername, + unsigned int argc, char *argv[], dns_dlzdb_t **dbp) +{ + dns_dlzimplementation_t *impinfo; + isc_result_t result; + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(dlzname != NULL); + REQUIRE(drivername != NULL); + REQUIRE(mctx != NULL); + + /* write log message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "Loading '%s' using driver %s", dlzname, drivername); + + /* lock the dlz_implementations list so we can search it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_read); + + /* search for the driver implementation */ + impinfo = dlz_impfind(drivername); + if (impinfo == NULL) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "unsupported DLZ database driver '%s'." + " %s not loaded.", + drivername, dlzname); + + return (ISC_R_NOTFOUND); + } + + /* Allocate memory to hold the DLZ database driver */ + (*dbp) = isc_mem_get(mctx, sizeof(dns_dlzdb_t)); + if ((*dbp) == NULL) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + return (ISC_R_NOMEMORY); + } + + /* Make sure memory region is set to all 0's */ + memset((*dbp), 0, sizeof(dns_dlzdb_t)); + + (*dbp)->implementation = impinfo; + + /* Create a new database using implementation 'drivername'. */ + result = ((impinfo->methods->create)(mctx, dlzname, argc, argv, + impinfo->driverarg, + &(*dbp)->dbdata)); + + /* mark the DLZ driver as valid */ + if (result == ISC_R_SUCCESS) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + (*dbp)->magic = DNS_DLZ_MAGIC; + isc_mem_attach(mctx, &(*dbp)->mctx); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "DLZ driver loaded successfully."); + return (ISC_R_SUCCESS); + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "DLZ driver failed to load."); + } + + /* impinfo->methods->create failed. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t)); + return (result); +} + +void +dns_dlzdestroy(dns_dlzdb_t **dbp) { + isc_mem_t *mctx; + dns_dlzdestroy_t destroy; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unloading DLZ driver."); + + /* + * Perform checks to make sure data is as we expect it to be. + */ + REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp)); + + /* call the drivers destroy method */ + if ((*dbp) != NULL) { + mctx = (*dbp)->mctx; + destroy = (*dbp)->implementation->methods->destroy; + (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata); + /* return memory */ + isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t)); + isc_mem_detach(&mctx); + } + + *dbp = NULL; +} + + +isc_result_t +dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels, + dns_db_t **dbp) +{ + dns_fixedname_t fname; + dns_name_t *zonename; + unsigned int namelabels; + unsigned int i; + isc_result_t result; + dns_dlzfindzone_t findzone; + dns_dlzdb_t *dlzdatabase; + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(DNS_DLZ_VALID(view->dlzdatabase)); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + /* setup a "fixed" dns name */ + dns_fixedname_init(&fname); + zonename = dns_fixedname_name(&fname); + + /* count the number of labels in the name */ + namelabels = dns_name_countlabels(name); + + /* + * loop through starting with the longest domain name and + * trying shorter names portions of the name until we find a + * match, have an error, or are below the 'minlabels' + * threshold. minlabels is 0, if the standard database didn't + * have a zone name match. Otherwise minlables is the number + * of labels in that name. We need to beat that for a + * "better" match for the DLZ database to be authoritative + * instead of the standard database. + */ + for (i = namelabels; i > minlabels && i > 1; i--) { + if (i == namelabels) { + result = dns_name_copy(name, zonename, NULL); + if (result != ISC_R_SUCCESS) + return (result); + } else + dns_name_split(name, i, NULL, zonename); + + /* ask SDLZ driver if the zone is supported */ + dlzdatabase = view->dlzdatabase; + findzone = dlzdatabase->implementation->methods->findzone; + result = (*findzone)(dlzdatabase->implementation->driverarg, + dlzdatabase->dbdata, dlzdatabase->mctx, + view->rdclass, zonename, dbp); + if (result != ISC_R_NOTFOUND) + return (result); + } + return (ISC_R_NOTFOUND); +} + +/*% + * Registers a DLZ driver. This basically just adds the dlz + * driver to the list of available drivers in the dlz_implementations list. + */ +isc_result_t +dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, + void *driverarg, isc_mem_t *mctx, + dns_dlzimplementation_t **dlzimp) +{ + + dns_dlzimplementation_t *dlz_imp; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering DLZ driver '%s'", drivername); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(drivername != NULL); + REQUIRE(methods != NULL); + REQUIRE(methods->create != NULL); + REQUIRE(methods->destroy != NULL); + REQUIRE(methods->findzone != NULL); + REQUIRE(mctx != NULL); + REQUIRE(dlzimp != NULL && *dlzimp == NULL); + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + /* lock the dlz_implementations list so we can modify it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_write); + + /* + * check that another already registered driver isn't using + * the same name + */ + dlz_imp = dlz_impfind(drivername); + if (dlz_imp != NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "DLZ Driver '%s' already registered", + drivername); + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + return (ISC_R_EXISTS); + } + + /* + * Allocate memory for a dlz_implementation object. Error if + * we cannot. + */ + dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t)); + if (dlz_imp == NULL) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + return (ISC_R_NOMEMORY); + } + + /* Make sure memory region is set to all 0's */ + memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t)); + + /* Store the data passed into this method */ + dlz_imp->name = drivername; + dlz_imp->methods = methods; + dlz_imp->mctx = NULL; + dlz_imp->driverarg = driverarg; + + /* attach the new dlz_implementation object to a memory context */ + isc_mem_attach(mctx, &dlz_imp->mctx); + + /* + * prepare the dlz_implementation object to be put in a list, + * and append it to the list + */ + ISC_LINK_INIT(dlz_imp, link); + ISC_LIST_APPEND(dlz_implementations, dlz_imp, link); + + /* Unlock the dlz_implementations list. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + + /* Pass back the dlz_implementation that we created. */ + *dlzimp = dlz_imp; + + return (ISC_R_SUCCESS); +} + +/*% + * Helper function for dns_dlzstrtoargv(). + * Pardon the gratuitous recursion. + */ +static isc_result_t +dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, + char ***argvp, unsigned int n) +{ + isc_result_t result; + + restart: + /* Discard leading whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + + if (*s == '\0') { + /* We have reached the end of the string. */ + *argcp = n; + *argvp = isc_mem_get(mctx, n * sizeof(char *)); + if (*argvp == NULL) + return (ISC_R_NOMEMORY); + } else { + char *p = s; + while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') { + if (*p == '\n') { + *p = ' '; + goto restart; + } + p++; + } + + /* do "grouping", items between { and } are one arg */ + if (*p == '{') { + char *t = p; + /* + * shift all characters to left by 1 to get rid of '{' + */ + while (*t != '\0') { + t++; + *(t-1) = *t; + } + while (*p != '\0' && *p != '}') { + p++; + } + /* get rid of '}' character */ + if (*p == '}') { + *p = '\0'; + p++; + } + /* normal case, no "grouping" */ + } else if (*p != '\0') + *p++ = '\0'; + + result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1); + if (result != ISC_R_SUCCESS) + return (result); + (*argvp)[n] = s; + } + return (ISC_R_SUCCESS); +} + +/*% + * Tokenize the string "s" into whitespace-separated words, + * return the number of words in '*argcp' and an array + * of pointers to the words in '*argvp'. The caller + * must free the array using isc_mem_put(). The string + * is modified in-place. + */ +isc_result_t +dns_dlzstrtoargv(isc_mem_t *mctx, char *s, + unsigned int *argcp, char ***argvp) +{ + return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0)); +} + +/*% + * Unregisters a DLZ driver. This basically just removes the dlz + * driver from the list of available drivers in the dlz_implementations list. + */ +void +dns_dlzunregister(dns_dlzimplementation_t **dlzimp) { + dns_dlzimplementation_t *dlz_imp; + isc_mem_t *mctx; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering DLZ driver."); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(dlzimp != NULL && *dlzimp != NULL); + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + dlz_imp = *dlzimp; + + /* lock the dlz_implementations list so we can modify it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_write); + + /* remove the dlz_implementation object from the list */ + ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link); + mctx = dlz_imp->mctx; + + /* + * return the memory back to the available memory pool and + * remove it from the memory context. + */ + isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t)); + isc_mem_detach(&mctx); + + /* Unlock the dlz_implementations list. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); +} diff --git a/lib/dns/include/dns/dlz.h b/lib/dns/include/dns/dlz.h new file mode 100644 index 00000000000..e228ea2717f --- /dev/null +++ b/lib/dns/include/dns/dlz.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: dlz.h,v 1.2 2005/09/05 00:11:02 marka Exp $ */ + +/*! \file */ + +#ifndef DLZ_H +#define DLZ_H 1 + +/***** + ***** Module Info + *****/ + +/* + * DLZ Interface + * + * The DLZ interface allows zones to be looked up using a driver instead of + * Bind's default in memory zone table. + * + * + * Reliability: + * No anticipated impact. + * + * Resources: + * + * Security: + * No anticipated impact. + * + * Standards: + * None. + */ + +/***** + ***** Imports + *****/ + +#include +#include +#include + +#include + +ISC_LANG_BEGINDECLS + +/*** + *** Types + ***/ + +#define DNS_DLZ_MAGIC ISC_MAGIC('D','L','Z','D') +#define DNS_DLZ_VALID(dlz) ISC_MAGIC_VALID(dlz, DNS_DLZ_MAGIC) + +typedef isc_result_t +(*dns_dlzallowzonexfr_t)(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, + isc_sockaddr_t *clientaddr, + dns_db_t **dbp); + +/*%< + * Method prototype. Drivers implementing the DLZ interface MUST + * supply an allow zone transfer method. This method is called when + * the DNS server is performing a zone transfer query. The driver's + * method should return ISC_R_SUCCESS and a database pointer to the + * name server if the zone is supported by the database, and zone + * transfer is allowed. Otherwise it will return ISC_R_NOTFOUND if + * the zone is not supported by the database, or ISC_R_NOPERM if zone + * transfers are not allowed. If an error occurs it should return a + * result code indicating the type of error. + */ + +typedef isc_result_t +(*dns_dlzcreate_t)(isc_mem_t *mctx, const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata); + +/*%< + * Method prototype. Drivers implementing the DLZ interface MUST + * supply a create method. This method is called when the DNS server + * is starting up and creating drivers for use later. + */ + +typedef void +(*dns_dlzdestroy_t)(void *driverarg, void **dbdata); + +/*%< + * Method prototype. Drivers implementing the DLZ interface MUST + * supply a destroy method. This method is called when the DNS server + * is shuting down and no longer needs the driver. + */ + +typedef isc_result_t +(*dns_dlzfindzone_t)(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, + dns_db_t **dbp); + +/*%< + + * Method prototype. Drivers implementing the DLZ interface MUST + * supply a find zone method. This method is called when the DNS + * server is performing a query. The find zone method will be called + * with the longest possible name first, and continue to be called + * with successively shorter domain names, until any of the following + * occur: + * + * \li 1) a match is found, and the function returns (ISC_R_SUCCESS) + * + * \li 2) a problem occurs, and the functions returns anything other + * than (ISC_R_NOTFOUND) + * \li 3) we run out of domain name labels. I.E. we have tried the + * shortest domain name + * \li 4) the number of labels in the domain name is less than + * min_lables for dns_dlzfindzone + * + * The driver's find zone method should return ISC_R_SUCCESS and a + * database pointer to the name server if the zone is supported by the + * database. Otherwise it will return ISC_R_NOTFOUND, and a null + * pointer if the zone is not supported. If an error occurs it should + * return a result code indicating the type of error. + */ + +/*% the methods supplied by a DLZ driver */ +typedef struct dns_dlzmethods { + dns_dlzcreate_t create; + dns_dlzdestroy_t destroy; + dns_dlzfindzone_t findzone; + dns_dlzallowzonexfr_t allowzonexfr; +} dns_dlzmethods_t; + +/*% information about a DLZ driver */ +struct dns_dlzimplementation { + const char *name; + const dns_dlzmethods_t *methods; + isc_mem_t *mctx; + void *driverarg; + ISC_LINK(dns_dlzimplementation_t) link; +}; + +/*% an instance of a DLZ driver */ +struct dns_dlzdb { + unsigned int magic; + isc_mem_t *mctx; + dns_dlzimplementation_t *implementation; + void *dbdata; +}; + + +/*** + *** Method declarations + ***/ + +isc_result_t +dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name, + isc_sockaddr_t *clientaddr, dns_db_t **dbp); + +/*%< + * This method is called when the DNS server is performing a zone + * transfer query. It will call the DLZ driver's allow zone tranfer + * method. + */ + +isc_result_t +dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, + const char *drivername, unsigned int argc, + char *argv[], dns_dlzdb_t **dbp); + +/*%< + * This method is called when the DNS server is starting up and + * creating drivers for use later. It will search the DLZ driver list + * for 'drivername' and return a DLZ driver via dbp if a match is + * found. If the DLZ driver supplies a create method, this function + * will call it. + */ + +void +dns_dlzdestroy(dns_dlzdb_t **dbp); + +/*%< + * This method is called when the DNS server is shuting down and no + * longer needs the driver. If the DLZ driver supplies a destroy + * methods, this function will call it. + */ + +isc_result_t +dns_dlzfindzone(dns_view_t *view, dns_name_t *name, + unsigned int minlabels, dns_db_t **dbp); + +/*%< + * This method is called when the DNS server is performing a query. + * It will call the DLZ driver's find zone method. + */ + +isc_result_t +dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, + void *driverarg, isc_mem_t *mctx, + dns_dlzimplementation_t **dlzimp); + +/*%< + * Register a dynamically loadable zones (DLZ) driver for the database + * type 'drivername', implemented by the functions in '*methods'. + * + * dlzimp must point to a NULL dlz_implementation_t pointer. That is, + * dlzimp != NULL && *dlzimp == NULL. It will be assigned a value that + * will later be used to identify the driver when deregistering it. + */ + +isc_result_t +dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp); + +/*%< + * This method is called when the name server is starting up to parse + * the DLZ driver command line from named.conf. Basically it splits + * up a string into and argc / argv. The primary difference of this + * method is items between braces { } are considered only 1 word. for + * example the command line "this is { one grouped phrase } and this + * isn't" would be parsed into: + * + * \li argv[0]: "this" + * \li argv[1]: "is" + * \li argv{2]: " one grouped phrase " + * \li argv[3]: "and" + * \li argv[4]: "this" + * \li argv{5}: "isn't" + * + * braces should NOT be nested, more than one grouping in the command + * line is allowed. Notice, argv[2] has an extra space at the + * beginning and end. Extra spaces are not stripped between a + * grouping. You can do so in your driver if needed, or be sure not + * to put extra spaces before / after the braces. + */ + +void +dns_dlzunregister(dns_dlzimplementation_t **dlzimp); + +/*%< + * Removes the dlz driver from the list of registered dlz drivers. + * There must be no active dlz drivers of this type when this function + * is called. + */ + +ISC_LANG_ENDDECLS + +#endif /* DLZ_H */ diff --git a/lib/dns/include/dns/log.h b/lib/dns/include/dns/log.h index 312bcc68952..738b293fcf1 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: log.h,v 1.36 2005/04/29 00:22:59 marka Exp $ */ +/* $Id: log.h,v 1.37 2005/09/05 00:11:02 marka Exp $ */ /*! \file * \author Principal Authors: DCL */ @@ -71,6 +71,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGMODULE_DIFF (&dns_modules[23]) #define DNS_LOGMODULE_HINTS (&dns_modules[24]) #define DNS_LOGMODULE_ACACHE (&dns_modules[25]) +#define DNS_LOGMODULE_DLZ (&dns_modules[26]) ISC_LANG_BEGINDECLS diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index 656ac088573..586c4ef931e 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: name.h,v 1.116 2005/05/19 04:59:04 marka Exp $ */ +/* $Id: name.h,v 1.117 2005/09/05 00:11:03 marka Exp $ */ #ifndef DNS_NAME_H #define DNS_NAME_H 1 @@ -264,7 +264,6 @@ dns_name_hasbuffer(const dns_name_t *name); * \li ISC_FALSE 'name' does not have a dedicated buffer. */ - /*** *** Properties ***/ @@ -958,9 +957,9 @@ dns_name_split(dns_name_t *name, unsigned int suffixlabels, * Requires: *\li 'name' is a valid name. * - * \li 'suffixlabels' cannot exceed the number of labels in 'name'. + *\li 'suffixlabels' cannot exceed the number of labels in 'name'. * - *\li 'prefix' is a valid name or NULL, and cannot be read-only. + * \li 'prefix' is a valid name or NULL, and cannot be read-only. * *\li 'suffix' is a valid name or NULL, and cannot be read-only. * diff --git a/lib/dns/include/dns/sdlz.h b/lib/dns/include/dns/sdlz.h new file mode 100644 index 00000000000..61ae8ad01a0 --- /dev/null +++ b/lib/dns/include/dns/sdlz.h @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: sdlz.h,v 1.2 2005/09/05 00:11:03 marka Exp $ */ + +/*! \file */ + +#ifndef SDLZ_H +#define SDLZ_H 1 + +#include + +ISC_LANG_BEGINDECLS + +#define DNS_SDLZFLAG_THREADSAFE 0x00000001U +#define DNS_SDLZFLAG_RELATIVEOWNER 0x00000002U +#define DNS_SDLZFLAG_RELATIVERDATA 0x00000004U + + /* A simple DLZ database. */ +typedef struct dns_sdlz_db dns_sdlz_db_t; + + /* A simple DLZ database lookup in progress. */ +typedef struct dns_sdlzlookup dns_sdlzlookup_t; + + /* A simple DLZ database traversal in progress. */ +typedef struct dns_sdlzallnodes dns_sdlzallnodes_t; + + +typedef isc_result_t +(*dns_sdlzallnodesfunc_t)(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface may + * supply an all nodes method. This method is called when the DNS + * server is performing a zone transfer query, after the allow zone + * transfer method has been called. This method is only called if the + * allow zone transfer method returned ISC_R_SUCCESS. This method and + * the allow zone transfer method are both required for zone transfers + * to be supported. If the driver generates data dynamically (instead + * of searching in a database for it) it should not implement this + * function as a zone transfer would be meaningless. A SDLZ driver + * does not have to implement an all nodes method. + */ + +typedef isc_result_t +(*dns_sdlzallowzonexfr_t)(void *driverarg, void *dbdata, const char *name, + const char *client); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface may + * supply an allow zone transfer method. This method is called when + * the DNS server is performing a zone transfer query, before the all + * nodes method can be called. This method and the all node method + * are both required for zone transfers to be supported. If the + * driver generates data dynamically (instead of searching in a + * database for it) it should not implement this function as a zone + * transfer would be meaningless. A SDLZ driver does not have to + * implement an allow zone transfer method. + * + * This method should return ISC_R_SUCCESS if the zone is supported by + * the database and a zone transfer is allowed for the specified + * client. If the zone is supported by the database, but zone + * transfers are not allowed for the specified client this method + * should return ISC_R_NOPERM.. Lastly the method should return + * ISC_R_NOTFOUND if the zone is not supported by the database. If an + * error occurs it should return a result code indicating the type of + * error. + */ + +typedef isc_result_t +(*dns_sdlzauthorityfunc_t)(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface may + * supply an authority method. This method is called when the DNS + * server is performing a query, after both the find zone and lookup + * methods have been called. This method is required if the lookup + * function does not supply authority information for the dns + * record. A SDLZ driver does not have to implement an authority + * method. + */ + +typedef isc_result_t +(*dns_sdlzcreate_t)(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface may + * supply a create method. This method is called when the DNS server + * is starting up and creating drivers for use later. A SDLZ driver + * does not have to implement a create method. + */ + +typedef void +(*dns_sdlzdestroy_t)(void *driverarg, void *dbdata); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface may + * supply a destroy method. This method is called when the DNS server + * is shuting down and no longer needs the driver. A SDLZ driver does + * not have to implement a destroy method. + */ + +typedef isc_result_t +(*dns_sdlzfindzone_t)(void *driverarg, void *dbdata, const char *name); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface MUST + * supply a find zone method. This method is called when the DNS + * server is performing a query to to determine if 'name' is a + * supported dns zone. The find zone method will be called with the + * longest possible name first, and continue to be called with + * successively shorter domain names, until any of the following + * occur: + * + * \li 1) the function returns (ISC_R_SUCCESS) indicating a zone name + * match. + * + * \li 2) a problem occurs, and the functions returns anything other than + * (ISC_R_NOTFOUND) + * + * \li 3) we run out of domain name labels. I.E. we have tried the + * shortest domain name + * + * \li 4) the number of labels in the domain name is less than min_lables + * for dns_dlzfindzone + * + * The driver's find zone method should return ISC_R_SUCCESS if the + * zone is supported by the database. Otherwise it should return + * ISC_R_NOTFOUND, if the zone is not supported. If an error occurs + * it should return a result code indicating the type of error. + */ + +typedef isc_result_t +(*dns_sdlzlookupfunc_t)(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup); + +/*%< + * Method prototype. Drivers implementing the SDLZ interface MUST + * supply a lookup method. This method is called when the DNS server + * is performing a query, after the find zone and before any other + * methods have been called. This function returns record DNS record + * information using the dns_sdlz_putrr and dns_sdlz_putsoa functions. + * If this function supplies authority information for the DNS record + * the authority method is not required. If it does not, the + * authority function is required. A SDLZ driver must implement a + * lookup method. + */ + +typedef struct dns_sdlzmethods { + dns_sdlzcreate_t create; + dns_sdlzdestroy_t destroy; + dns_sdlzfindzone_t findzone; + dns_sdlzlookupfunc_t lookup; + dns_sdlzauthorityfunc_t authority; + dns_sdlzallnodesfunc_t allnodes; + dns_sdlzallowzonexfr_t allowzonexfr; +} dns_sdlzmethods_t; + +isc_result_t +dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods, + void *driverarg, unsigned int flags, isc_mem_t *mctx, + dns_sdlzimplementation_t **sdlzimp); +/*%< + * Register a dynamically loadable zones (dlz) driver for the database + * type 'drivername', implemented by the functions in '*methods'. + * + * sdlzimp must point to a NULL dns_sdlzimplementation_t pointer. + * That is, sdlzimp != NULL && *sdlzimp == NULL. It will be assigned + * a value that will later be used to identify the driver when + * deregistering it. + */ + +void +dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp); + +/*%< + * Removes the sdlz driver from the list of registered sdlz drivers. + * There must be no active sdlz drivers of this type when this + * function is called. + */ + +isc_result_t +dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name, + const char *type, dns_ttl_t ttl, const char *data); +/*%< + * Add a single resource record to the allnodes structure to be later + * parsed into a zone transfer response. + */ + +isc_result_t +dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl, + const char *data); +/*%< + * Add a single resource record to the lookup structure to be later + * parsed into a query response. + */ + +isc_result_t +dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname, + isc_uint32_t serial); +/*%< + * This function may optionally be called from the 'authority' + * callback to simplify construction of the SOA record for 'zone'. It + * will provide a SOA listing 'mname' as as the master server and + * 'rname' as the responsible person mailbox. It is the + * responsibility of the driver to increment the serial number between + * responses if necessary. All other SOA fields will have reasonable + * default values. + */ + + +ISC_LANG_ENDDECLS + +#endif /* SDLZ_H */ diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index cf77d16a1d6..5763fe65cce 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: types.h,v 1.116 2005/06/20 01:03:54 marka Exp $ */ +/* $Id: types.h,v 1.117 2005/09/05 00:11:03 marka Exp $ */ #ifndef DNS_TYPES_H #define DNS_TYPES_H 1 @@ -53,6 +53,9 @@ typedef void dns_dbload_t; typedef void dns_dbnode_t; typedef struct dns_dbtable dns_dbtable_t; typedef void dns_dbversion_t; +typedef struct dns_dlzimplementation dns_dlzimplementation_t; +typedef struct dns_dlzdb dns_dlzdb_t; +typedef struct dns_sdlzimplementation dns_sdlzimplementation_t; typedef struct dns_decompress dns_decompress_t; typedef struct dns_dispatch dns_dispatch_t; typedef struct dns_dispatchevent dns_dispatchevent_t; diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 7cc4e89185f..d967dcf9956 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.h,v 1.96 2005/07/18 05:58:59 marka Exp $ */ +/* $Id: view.h,v 1.97 2005/09/05 00:11:04 marka Exp $ */ #ifndef DNS_VIEW_H #define DNS_VIEW_H 1 @@ -84,6 +84,7 @@ struct dns_view { dns_rdataclass_t rdclass; char * name; dns_zt_t * zonetable; + dns_dlzdb_t * dlzdatabase; dns_resolver_t * resolver; dns_adb_t * adb; dns_requestmgr_t * requestmgr; diff --git a/lib/dns/log.c b/lib/dns/log.c index d2bc330aa1f..afaaef10380 100644 --- a/lib/dns/log.c +++ b/lib/dns/log.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: log.c,v 1.39 2005/04/29 00:22:48 marka Exp $ */ +/* $Id: log.c,v 1.40 2005/09/05 00:11:01 marka Exp $ */ /*! \file */ @@ -77,6 +77,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = { { "dns/diff", 0 }, { "dns/hints", 0 }, { "dns/acache", 0 }, + { "dns/dlz", 0 }, { NULL, 0 } }; diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c new file mode 100644 index 00000000000..a8d8adcf77f --- /dev/null +++ b/lib/dns/sdlz.c @@ -0,0 +1,1781 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * 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 STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET 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. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * 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 ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER 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. + */ + +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: sdlz.c,v 1.2 2005/09/05 00:11:02 marka Exp $ */ + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdatalist_p.h" + +/* + * Private Types + */ + +struct dns_sdlzimplementation { + const dns_sdlzmethods_t *methods; + isc_mem_t *mctx; + void *driverarg; + unsigned int flags; + isc_mutex_t driverlock; + dns_dlzimplementation_t *dlz_imp; +}; + +struct dns_sdlz_db { + /* Unlocked */ + dns_db_t common; + void *dbdata; + dns_sdlzimplementation_t *dlzimp; + isc_mutex_t refcnt_lock; + /* Locked */ + unsigned int references; +}; + +struct dns_sdlzlookup { + /* Unlocked */ + unsigned int magic; + dns_sdlz_db_t *sdlz; + ISC_LIST(dns_rdatalist_t) lists; + ISC_LIST(isc_buffer_t) buffers; + dns_name_t *name; + ISC_LINK(dns_sdlzlookup_t) link; + isc_mutex_t lock; + dns_rdatacallbacks_t callbacks; + /* Locked */ + unsigned int references; +}; + +typedef struct dns_sdlzlookup dns_sdlznode_t; + +struct dns_sdlzallnodes { + dns_dbiterator_t common; + ISC_LIST(dns_sdlznode_t) nodelist; + dns_sdlznode_t *current; + dns_sdlznode_t *origin; +}; + +typedef dns_sdlzallnodes_t sdlz_dbiterator_t; + +typedef struct sdlz_rdatasetiter { + dns_rdatasetiter_t common; + dns_rdatalist_t *current; +} sdlz_rdatasetiter_t; + + +#define SDLZDB_MAGIC ISC_MAGIC('D', 'L', 'Z', 'S') + +/* + * Note that "impmagic" is not the first four bytes of the struct, so + * ISC_MAGIC_VALID cannot be used. + */ + +#define VALID_SDLZDB(sdlzdb) ((sdlzdb) != NULL && \ + (sdlzdb)->common.impmagic == SDLZDB_MAGIC) + +#define SDLZLOOKUP_MAGIC ISC_MAGIC('D','L','Z','L') +#define VALID_SDLZLOOKUP(sdlzl) ISC_MAGIC_VALID(sdlzl, SDLZLOOKUP_MAGIC) +#define VALID_SDLZNODE(sdlzn) VALID_SDLZLOOKUP(sdlzn) + +/* These values are taken from RFC 1537 */ +#define SDLZ_DEFAULT_REFRESH (60 * 60 * 8) +#define SDLZ_DEFAULT_RETRY (60 * 60 * 2) +#define SDLZ_DEFAULT_EXPIRE (60 * 60 * 24 * 7) +#define SDLZ_DEFAULT_MINIMUM (60 * 60 * 24) + +/* This is a reasonable value */ +#define SDLZ_DEFAULT_TTL (60 * 60 * 24) + +static int dummy; + +#define MAYBE_LOCK(imp) \ + do { \ + unsigned int flags = imp->flags; \ + if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ + LOCK(&imp->driverlock); \ + } while (0) + +#define MAYBE_UNLOCK(imp) \ + do { \ + unsigned int flags = imp->flags; \ + if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ + UNLOCK(&imp->driverlock); \ + } while (0) + +/* + * Forward references. Try to keep these to a minimum. + */ + +static void list_tordataset(dns_rdatalist_t *rdatalist, + dns_db_t *db, dns_dbnode_t *node, + dns_rdataset_t *rdataset); + +static void detachnode(dns_db_t *db, dns_dbnode_t **targetp); + +static void dbiterator_destroy(dns_dbiterator_t **iteratorp); +static isc_result_t dbiterator_first(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_last(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, + dns_name_t *name); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_next(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_current(dns_dbiterator_t *iterator, + dns_dbnode_t **nodep, + dns_name_t *name); +static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator); +static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, + dns_name_t *name); + +static dns_dbiteratormethods_t dbiterator_methods = { + dbiterator_destroy, + dbiterator_first, + dbiterator_last, + dbiterator_seek, + dbiterator_prev, + dbiterator_next, + dbiterator_current, + dbiterator_pause, + dbiterator_origin +}; + +/* + * Utility functions + */ + +/*% Converts the input string to lowercase, in place. */ + +static void +dns_sdlz_tolower(char *str) { + + unsigned int len = strlen(str); + unsigned int i; + + for (i = 0; i < len; i++) { + if (str[i] >= 'A' && str[i] <= 'Z') + str[i] += 32; + } + +} + +static inline unsigned int +initial_size(const char *data) { + unsigned int len = (strlen(data) / 64) + 1; + return (len * 64 + 64); +} + +/* + * Rdataset Iterator Methods. These methods were "borrowed" from the SDB + * driver interface. See the SDB driver interface documentation for more info. + */ + +static void +rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) { + sdlz_rdatasetiter_t *sdlziterator = + (sdlz_rdatasetiter_t *)(*iteratorp); + + detachnode(sdlziterator->common.db, &sdlziterator->common.node); + isc_mem_put(sdlziterator->common.db->mctx, sdlziterator, + sizeof(sdlz_rdatasetiter_t)); + *iteratorp = NULL; +} + +static isc_result_t +rdatasetiter_first(dns_rdatasetiter_t *iterator) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)iterator->node; + + if (ISC_LIST_EMPTY(sdlznode->lists)) + return (ISC_R_NOMORE); + sdlziterator->current = ISC_LIST_HEAD(sdlznode->lists); + return (ISC_R_SUCCESS); +} + +static isc_result_t +rdatasetiter_next(dns_rdatasetiter_t *iterator) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + + sdlziterator->current = ISC_LIST_NEXT(sdlziterator->current, link); + if (sdlziterator->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static void +rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + + list_tordataset(sdlziterator->current, iterator->db, iterator->node, + rdataset); +} + +static dns_rdatasetitermethods_t rdatasetiter_methods = { + rdatasetiter_destroy, + rdatasetiter_first, + rdatasetiter_next, + rdatasetiter_current +}; + +/* + * DB routines. These methods were "borrowed" from the SDB driver interface. + * See the SDB driver interface documentation for more info. + */ + +static void +attach(dns_db_t *source, dns_db_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *) source; + + REQUIRE(VALID_SDLZDB(sdlz)); + + LOCK(&sdlz->refcnt_lock); + REQUIRE(sdlz->references > 0); + sdlz->references++; + UNLOCK(&sdlz->refcnt_lock); + + *targetp = source; +} + +static void +destroy(dns_sdlz_db_t *sdlz) { + isc_mem_t *mctx; + mctx = sdlz->common.mctx; + + sdlz->common.magic = 0; + sdlz->common.impmagic = 0; + + isc_mutex_destroy(&sdlz->refcnt_lock); + + dns_name_free(&sdlz->common.origin, mctx); + + isc_mem_put(mctx, sdlz, sizeof(dns_sdlz_db_t)); + isc_mem_detach(&mctx); +} + +static void +detach(dns_db_t **dbp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)(*dbp); + isc_boolean_t need_destroy = ISC_FALSE; + + REQUIRE(VALID_SDLZDB(sdlz)); + LOCK(&sdlz->refcnt_lock); + REQUIRE(sdlz->references > 0); + sdlz->references--; + if (sdlz->references == 0) + need_destroy = ISC_TRUE; + UNLOCK(&sdlz->refcnt_lock); + + if (need_destroy) + destroy(sdlz); + + *dbp = NULL; +} + +static isc_result_t +beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) { + UNUSED(db); + UNUSED(addp); + UNUSED(dbloadp); + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +endload(dns_db_t *db, dns_dbload_t **dbloadp) { + UNUSED(db); + UNUSED(dbloadp); + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +dump(dns_db_t *db, dns_dbversion_t *version, const char *filename, + dns_masterformat_t masterformat) +{ + UNUSED(db); + UNUSED(version); + UNUSED(filename); + UNUSED(masterformat); + return (ISC_R_NOTIMPLEMENTED); +} + +static void +currentversion(dns_db_t *db, dns_dbversion_t **versionp) { + REQUIRE(versionp != NULL && *versionp == NULL); + + UNUSED(db); + + *versionp = (void *) &dummy; + return; +} + +static isc_result_t +newversion(dns_db_t *db, dns_dbversion_t **versionp) { + UNUSED(db); + UNUSED(versionp); + + return (ISC_R_NOTIMPLEMENTED); +} + +static void +attachversion(dns_db_t *db, dns_dbversion_t *source, + dns_dbversion_t **targetp) +{ + REQUIRE(source != NULL && source == (void *) &dummy); + + UNUSED(db); + UNUSED(source); + UNUSED(targetp); + *targetp = source; +} + +static void +closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { + REQUIRE(versionp != NULL && *versionp == (void *) &dummy); + REQUIRE(commit == ISC_FALSE); + + UNUSED(db); + UNUSED(commit); + + *versionp = NULL; +} + +static isc_result_t +createnode(dns_sdlz_db_t *sdlz, dns_sdlznode_t **nodep) { + dns_sdlznode_t *node; + isc_result_t result; + + node = isc_mem_get(sdlz->common.mctx, sizeof(dns_sdlznode_t)); + if (node == NULL) + return (ISC_R_NOMEMORY); + + node->sdlz = NULL; + attach((dns_db_t *)sdlz, (dns_db_t **)&node->sdlz); + ISC_LIST_INIT(node->lists); + ISC_LIST_INIT(node->buffers); + ISC_LINK_INIT(node, link); + node->name = NULL; + result = isc_mutex_init(&node->lock); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + isc_mem_put(sdlz->common.mctx, node, sizeof(dns_sdlznode_t)); + return (ISC_R_UNEXPECTED); + } + dns_rdatacallbacks_init(&node->callbacks); + node->references = 1; + node->magic = SDLZLOOKUP_MAGIC; + + *nodep = node; + return (ISC_R_SUCCESS); +} + +static void +destroynode(dns_sdlznode_t *node) { + dns_rdatalist_t *list; + dns_rdata_t *rdata; + isc_buffer_t *b; + dns_sdlz_db_t *sdlz; + dns_db_t *db; + isc_mem_t *mctx; + + sdlz = node->sdlz; + mctx = sdlz->common.mctx; + + while (!ISC_LIST_EMPTY(node->lists)) { + list = ISC_LIST_HEAD(node->lists); + while (!ISC_LIST_EMPTY(list->rdata)) { + rdata = ISC_LIST_HEAD(list->rdata); + ISC_LIST_UNLINK(list->rdata, rdata, link); + isc_mem_put(mctx, rdata, sizeof(dns_rdata_t)); + } + ISC_LIST_UNLINK(node->lists, list, link); + isc_mem_put(mctx, list, sizeof(dns_rdatalist_t)); + } + + while (!ISC_LIST_EMPTY(node->buffers)) { + b = ISC_LIST_HEAD(node->buffers); + ISC_LIST_UNLINK(node->buffers, b, link); + isc_buffer_free(&b); + } + + if (node->name != NULL) { + dns_name_free(node->name, mctx); + isc_mem_put(mctx, node->name, sizeof(dns_name_t)); + } + DESTROYLOCK(&node->lock); + node->magic = 0; + isc_mem_put(mctx, node, sizeof(dns_sdlznode_t)); + db = &sdlz->common; + detach(&db); +} + +static isc_result_t +findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, + dns_dbnode_t **nodep) +{ + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node = NULL; + isc_result_t result; + isc_buffer_t b; + char namestr[DNS_NAME_MAXTEXT + 1]; + isc_buffer_t b2; + char zonestr[DNS_NAME_MAXTEXT + 1]; + isc_boolean_t isorigin; + dns_sdlzauthorityfunc_t authority; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(create == ISC_FALSE); + REQUIRE(nodep != NULL && *nodep == NULL); + + UNUSED(name); + UNUSED(create); + + isc_buffer_init(&b, namestr, sizeof(namestr)); + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVEOWNER) != 0) { + dns_name_t relname; + unsigned int labels; + + labels = dns_name_countlabels(name) - + dns_name_countlabels(&db->origin); + dns_name_init(&relname, NULL); + dns_name_getlabelsequence(name, 0, labels, &relname); + result = dns_name_totext(&relname, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + } else { + result = dns_name_totext(name, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + } + isc_buffer_putuint8(&b, 0); + + isc_buffer_init(&b2, zonestr, sizeof(zonestr)); + result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b2); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b2, 0); + + result = createnode(sdlz, &node); + if (result != ISC_R_SUCCESS) + return (result); + + isorigin = dns_name_equal(name, &sdlz->common.origin); + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(zonestr); + dns_sdlz_tolower(namestr); + + MAYBE_LOCK(sdlz->dlzimp); + + /* try to lookup the host (namestr) */ + result = sdlz->dlzimp->methods->lookup(zonestr, namestr, + sdlz->dlzimp->driverarg, + sdlz->dbdata, node); + + /* + * if the host (namestr) was not found, try to lookup a + * "wildcard" host. + */ + if (result != ISC_R_SUCCESS) { + result = sdlz->dlzimp->methods->lookup(zonestr, "*", + sdlz->dlzimp->driverarg, + sdlz->dbdata, node); + } + + MAYBE_UNLOCK(sdlz->dlzimp); + + if (result != ISC_R_SUCCESS && !isorigin) { + destroynode(node); + return (result); + } + + if (isorigin && sdlz->dlzimp->methods->authority != NULL) { + MAYBE_LOCK(sdlz->dlzimp); + authority = sdlz->dlzimp->methods->authority; + result = (*authority)(zonestr, sdlz->dlzimp->driverarg, + sdlz->dbdata, node); + MAYBE_UNLOCK(sdlz->dlzimp); + if (result != ISC_R_SUCCESS && + result != ISC_R_NOTIMPLEMENTED) { + destroynode(node); + return (result); + } + } + + *nodep = node; + return (ISC_R_SUCCESS); +} + +static isc_result_t +findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, + isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + UNUSED(db); + UNUSED(name); + UNUSED(options); + UNUSED(now); + UNUSED(nodep); + UNUSED(foundname); + UNUSED(rdataset); + UNUSED(sigrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static void +attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node = (dns_sdlznode_t *)source; + + REQUIRE(VALID_SDLZDB(sdlz)); + + UNUSED(sdlz); + + LOCK(&node->lock); + INSIST(node->references > 0); + node->references++; + INSIST(node->references != 0); /* Catch overflow. */ + UNLOCK(&node->lock); + + *targetp = source; +} + +static void +detachnode(dns_db_t *db, dns_dbnode_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node; + isc_boolean_t need_destroy = ISC_FALSE; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(targetp != NULL && *targetp != NULL); + + UNUSED(sdlz); + + node = (dns_sdlznode_t *)(*targetp); + + LOCK(&node->lock); + INSIST(node->references > 0); + node->references--; + if (node->references == 0) + need_destroy = ISC_TRUE; + UNLOCK(&node->lock); + + if (need_destroy) + destroynode(node); + + *targetp = NULL; +} + +static isc_result_t +expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { + UNUSED(db); + UNUSED(node); + UNUSED(now); + INSIST(0); + return (ISC_R_UNEXPECTED); +} + +static void +printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) { + UNUSED(db); + UNUSED(node); + UNUSED(out); + return; +} + +static isc_result_t +createiterator(dns_db_t *db, isc_boolean_t relative_names, + dns_dbiterator_t **iteratorp) +{ + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + sdlz_dbiterator_t *sdlziter; + isc_result_t result; + isc_buffer_t b; + char zonestr[DNS_NAME_MAXTEXT + 1]; + + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->allnodes == NULL) + return (ISC_R_NOTIMPLEMENTED); + + isc_buffer_init(&b, zonestr, sizeof(zonestr)); + result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b, 0); + + sdlziter = isc_mem_get(sdlz->common.mctx, sizeof(sdlz_dbiterator_t)); + if (sdlziter == NULL) + return (ISC_R_NOMEMORY); + + sdlziter->common.methods = &dbiterator_methods; + sdlziter->common.db = NULL; + dns_db_attach(db, &sdlziter->common.db); + sdlziter->common.relative_names = relative_names; + sdlziter->common.magic = DNS_DBITERATOR_MAGIC; + ISC_LIST_INIT(sdlziter->nodelist); + sdlziter->current = NULL; + sdlziter->origin = NULL; + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(zonestr); + + MAYBE_LOCK(sdlz->dlzimp); + result = sdlz->dlzimp->methods->allnodes(zonestr, + sdlz->dlzimp->driverarg, + sdlz->dbdata, sdlziter); + MAYBE_UNLOCK(sdlz->dlzimp); + if (result != ISC_R_SUCCESS) { + dns_dbiterator_t *iter = &sdlziter->common; + dbiterator_destroy(&iter); + return (result); + } + + if (sdlziter->origin != NULL) { + ISC_LIST_UNLINK(sdlziter->nodelist, sdlziter->origin, link); + ISC_LIST_PREPEND(sdlziter->nodelist, sdlziter->origin, link); + } + + *iteratorp = (dns_dbiterator_t *)sdlziter; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_stdtime_t now, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset) +{ + dns_rdatalist_t *list; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node; + + REQUIRE(VALID_SDLZNODE(node)); + + UNUSED(db); + UNUSED(version); + UNUSED(covers); + UNUSED(now); + UNUSED(sigrdataset); + + if (type == dns_rdatatype_sig) + return (ISC_R_NOTIMPLEMENTED); + + list = ISC_LIST_HEAD(sdlznode->lists); + while (list != NULL) { + if (list->type == type) + break; + list = ISC_LIST_NEXT(list, link); + } + if (list == NULL) + return (ISC_R_NOTFOUND); + + list_tordataset(list, db, node, rdataset); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_dbnode_t *node = NULL; + dns_fixedname_t fname; + dns_rdataset_t xrdataset; + dns_name_t *xname; + unsigned int nlabels, olabels; + isc_result_t result; + unsigned int i; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(nodep == NULL || *nodep == NULL); + REQUIRE(version == NULL || version == (void *) &dummy); + + UNUSED(options); + UNUSED(sdlz); + + if (!dns_name_issubdomain(name, &db->origin)) + return (DNS_R_NXDOMAIN); + + olabels = dns_name_countlabels(&db->origin); + nlabels = dns_name_countlabels(name); + + dns_fixedname_init(&fname); + xname = dns_fixedname_name(&fname); + + if (rdataset == NULL) + rdataset = &xrdataset; + + result = DNS_R_NXDOMAIN; + + for (i = olabels; i <= nlabels; i++) { + /* + * Unless this is an explicit lookup at the origin, don't + * look at the origin. + */ + if (i == olabels && i != nlabels) + continue; + + /* + * Look up the next label. + */ + dns_name_getlabelsequence(name, nlabels - i, i, xname); + result = findnode(db, xname, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) { + result = DNS_R_NXDOMAIN; + continue; + } + + /* + * Look for a DNAME at the current label, unless this is + * the qname. + */ + if (i < nlabels) { + result = findrdataset(db, node, version, + dns_rdatatype_dname, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + result = DNS_R_DNAME; + break; + } + } + + /* + * Look for an NS at the current label, unless this is the + * origin or glue is ok. + */ + if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) { + result = findrdataset(db, node, version, + dns_rdatatype_ns, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + if (i == nlabels && type == dns_rdatatype_any) + { + result = DNS_R_ZONECUT; + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL) + dns_rdataset_disassociate + (sigrdataset); + } else + result = DNS_R_DELEGATION; + break; + } + } + + /* + * If the current name is not the qname, add another label + * and try again. + */ + if (i < nlabels) { + destroynode(node); + node = NULL; + continue; + } + + /* + * If we're looking for ANY, we're done. + */ + if (type == dns_rdatatype_any) { + result = ISC_R_SUCCESS; + break; + } + + /* + * Look for the qtype. + */ + result = findrdataset(db, node, version, type, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) + break; + + /* + * Look for a CNAME + */ + if (type != dns_rdatatype_cname) { + result = findrdataset(db, node, version, + dns_rdatatype_cname, + 0, now, rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + result = DNS_R_CNAME; + break; + } + } + + result = DNS_R_NXRRSET; + break; + } + + if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + + if (foundname != NULL) { + isc_result_t xresult; + + xresult = dns_name_copy(xname, foundname, NULL); + if (xresult != ISC_R_SUCCESS) { + destroynode(node); + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + return (DNS_R_BADDB); + } + } + + if (nodep != NULL) + *nodep = node; + else if (node != NULL) + detachnode(db, &node); + + return (result); +} + +static isc_result_t +allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) +{ + sdlz_rdatasetiter_t *iterator; + + REQUIRE(version == NULL || version == &dummy); + + UNUSED(version); + UNUSED(now); + + iterator = isc_mem_get(db->mctx, sizeof(sdlz_rdatasetiter_t)); + if (iterator == NULL) + return (ISC_R_NOMEMORY); + + iterator->common.magic = DNS_RDATASETITER_MAGIC; + iterator->common.methods = &rdatasetiter_methods; + iterator->common.db = db; + iterator->common.node = NULL; + attachnode(db, node, &iterator->common.node); + iterator->common.version = version; + iterator->common.now = now; + + *iteratorp = (dns_rdatasetiter_t *)iterator; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *addedrdataset) +{ + UNUSED(db); + UNUSED(node); + UNUSED(version); + UNUSED(now); + UNUSED(rdataset); + UNUSED(options); + UNUSED(addedrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *newrdataset) +{ + UNUSED(db); + UNUSED(node); + UNUSED(version); + UNUSED(rdataset); + UNUSED(options); + UNUSED(newrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers) +{ + UNUSED(db); + UNUSED(node); + UNUSED(version); + UNUSED(type); + UNUSED(covers); + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_boolean_t +issecure(dns_db_t *db) { + UNUSED(db); + + return (ISC_FALSE); +} + +static unsigned int +nodecount(dns_db_t *db) { + UNUSED(db); + + return (0); +} + +static isc_boolean_t +ispersistent(dns_db_t *db) { + UNUSED(db); + return (ISC_TRUE); +} + +static void +overmem(dns_db_t *db, isc_boolean_t overmem) { + UNUSED(db); + UNUSED(overmem); +} + +static void +settask(dns_db_t *db, isc_task_t *task) { + UNUSED(db); + UNUSED(task); +} + + +static dns_dbmethods_t sdlzdb_methods = { + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + find, + findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, + NULL, + NULL, + NULL, + NULL +}; + +/* + * Database Iterator Methods. These methods were "borrowed" from the SDB + * driver interface. See the SDB driver interface documentation for more info. + */ + +static void +dbiterator_destroy(dns_dbiterator_t **iteratorp) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)(*iteratorp); + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)sdlziter->common.db; + + while (!ISC_LIST_EMPTY(sdlziter->nodelist)) { + dns_sdlznode_t *node; + node = ISC_LIST_HEAD(sdlziter->nodelist); + ISC_LIST_UNLINK(sdlziter->nodelist, node, link); + destroynode(node); + } + + dns_db_detach(&sdlziter->common.db); + isc_mem_put(sdlz->common.mctx, sdlziter, sizeof(sdlz_dbiterator_t)); + + *iteratorp = NULL; +} + +static isc_result_t +dbiterator_first(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_last(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_TAIL(sdlziter->nodelist); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist); + while (sdlziter->current != NULL) + if (dns_name_equal(sdlziter->current->name, name)) + return (ISC_R_SUCCESS); + return (ISC_R_NOTFOUND); +} + +static isc_result_t +dbiterator_prev(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_PREV(sdlziter->current, link); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_next(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link); + if (sdlziter->current == NULL) + return (ISC_R_NOMORE); + else + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, + dns_name_t *name) +{ + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + attachnode(iterator->db, sdlziter->current, nodep); + if (name != NULL) + return (dns_name_copy(sdlziter->current->name, name, NULL)); + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_pause(dns_dbiterator_t *iterator) { + UNUSED(iterator); + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { + UNUSED(iterator); + return (dns_name_copy(dns_rootname, name, NULL)); +} + +/* + * Rdataset Methods. These methods were "borrowed" from the SDB driver + * interface. See the SDB driver interface documentation for more info. + */ + +static void +disassociate(dns_rdataset_t *rdataset) { + dns_dbnode_t *node = rdataset->private5; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node; + dns_db_t *db = (dns_db_t *) sdlznode->sdlz; + + detachnode(db, &node); + isc__rdatalist_disassociate(rdataset); +} + +static void +rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { + dns_dbnode_t *node = source->private5; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node; + dns_db_t *db = (dns_db_t *) sdlznode->sdlz; + dns_dbnode_t *tempdb = NULL; + + isc__rdatalist_clone(source, target); + attachnode(db, node, &tempdb); + source->private5 = tempdb; +} + +static dns_rdatasetmethods_t rdataset_methods = { + disassociate, + isc__rdatalist_first, + isc__rdatalist_next, + isc__rdatalist_current, + rdataset_clone, + isc__rdatalist_count, + isc__rdatalist_addnoqname, + isc__rdatalist_getnoqname, + NULL, + NULL, + NULL +}; + +static void +list_tordataset(dns_rdatalist_t *rdatalist, + dns_db_t *db, dns_dbnode_t *node, + dns_rdataset_t *rdataset) +{ + /* + * The sdlz rdataset is an rdatalist with some additions. + * - private1 & private2 are used by the rdatalist. + * - private3 & private 4 are unused. + * - private5 is the node. + */ + + /* This should never fail. */ + RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == + ISC_R_SUCCESS); + + rdataset->methods = &rdataset_methods; + dns_db_attachnode(db, node, &rdataset->private5); +} + +/* + * SDLZ core methods. This is the core of the new DLZ functionality. + */ + +/*% + * Build a 'bind' database driver structure to be returned by + * either the find zone or the allow zone transfer method. + * This method is only available in this source file, it is + * not made available anywhere else. + */ + +static isc_result_t +dns_sdlzcreateDBP(isc_mem_t *mctx, void *driverarg, void *dbdata, + dns_name_t *name, dns_rdataclass_t rdclass, dns_db_t **dbp) +{ + isc_result_t result; + dns_sdlz_db_t *sdlzdb; + dns_sdlzimplementation_t *imp; + + /* check that things are as we expect */ + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(name != NULL); + + imp = (dns_sdlzimplementation_t *) driverarg; + + /* allocate and zero memory for driver structure */ + sdlzdb = isc_mem_get(mctx, sizeof(dns_sdlz_db_t)); + if (sdlzdb == NULL) + return (ISC_R_NOMEMORY); + memset(sdlzdb, 0, sizeof(dns_sdlz_db_t)); + + /* initialize and set origin */ + dns_name_init(&sdlzdb->common.origin, NULL); + result = dns_name_dupwithoffsets(name, mctx, &sdlzdb->common.origin); + if (result != ISC_R_SUCCESS) + goto mem_cleanup; + + /* initialize the reference count mutex */ + result = isc_mutex_init(&sdlzdb->refcnt_lock); + if (result != ISC_R_SUCCESS) + goto name_cleanup; + + /* set the rest of the database structure attributes */ + sdlzdb->dlzimp = imp; + sdlzdb->common.methods = &sdlzdb_methods; + sdlzdb->common.attributes = 0; + sdlzdb->common.rdclass = rdclass; + sdlzdb->common.mctx = NULL; + sdlzdb->dbdata = dbdata; + sdlzdb->references = 1; + + /* attach to the memory context */ + isc_mem_attach(mctx, &sdlzdb->common.mctx); + + /* mark structure as valid */ + sdlzdb->common.magic = DNS_DB_MAGIC; + sdlzdb->common.impmagic = SDLZDB_MAGIC; + *dbp = (dns_db_t *) sdlzdb; + + return (result); + + /* + * reference count mutex could not be initialized, clean up + * name memory + */ + name_cleanup: + dns_name_free(&sdlzdb->common.origin, mctx); + mem_cleanup: + isc_mem_put(mctx, sdlzdb, sizeof(dns_sdlz_db_t)); + return (result); +} + +static isc_result_t +dns_sdlzallowzonexfr(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, + isc_sockaddr_t *clientaddr, dns_db_t **dbp) +{ + isc_buffer_t b; + isc_buffer_t b2; + char namestr[DNS_NAME_MAXTEXT + 1]; + char clientstr[(sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255") + + 1]; + isc_netaddr_t netaddr; + isc_result_t result; + dns_sdlzimplementation_t *imp; + + /* + * Perform checks to make sure data is as we expect it to be. + */ + REQUIRE(driverarg != NULL); + REQUIRE(name != NULL); + REQUIRE(clientaddr != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + imp = (dns_sdlzimplementation_t *) driverarg; + + /* Convert DNS name to ascii text */ + isc_buffer_init(&b, namestr, sizeof(namestr)); + result = dns_name_totext(name, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b, 0); + + /* convert client address to ascii text */ + isc_buffer_init(&b2, clientstr, sizeof(clientstr)); + isc_netaddr_fromsockaddr(&netaddr, clientaddr); + result = isc_netaddr_totext(&netaddr, &b2); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b2, 0); + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(namestr); + dns_sdlz_tolower(clientstr); + + /* Call SDLZ driver's find zone method */ + if (imp->methods->allowzonexfr != NULL) { + MAYBE_LOCK(imp); + result = imp->methods->allowzonexfr(imp->driverarg, dbdata, + namestr, clientstr); + MAYBE_UNLOCK(imp); + /* + * if zone is supported and transfers allowed build a 'bind' + * database driver + */ + if (result == ISC_R_SUCCESS) + result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, + name, rdclass, dbp); + return (result); + } + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +dns_sdlzcreate(isc_mem_t *mctx, const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata) +{ + dns_sdlzimplementation_t *imp; + isc_result_t result = ISC_R_NOTFOUND; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Loading SDLZ driver."); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(driverarg != NULL); + REQUIRE(dlzname != NULL); + REQUIRE(dbdata != NULL); + UNUSED(mctx); + + imp = driverarg; + + /* If the create method exists, call it. */ + if (imp->methods->create != NULL) { + MAYBE_LOCK(imp); + result = imp->methods->create(dlzname, argc, argv, + imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + } + + /* Write debugging message to log */ + if (result == ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "SDLZ driver loaded successfully."); + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "SDLZ driver failed to load."); + } + + return (result); +} + +static void +dns_sdlzdestroy(void *driverdata, void **dbdata) +{ + + dns_sdlzimplementation_t *imp; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unloading SDLZ driver."); + + imp = driverdata; + + /* If the destroy method exists, call it. */ + if (imp->methods->destroy != NULL) { + MAYBE_LOCK(imp); + imp->methods->destroy(imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + } +} + +static isc_result_t +dns_sdlzfindzone(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_name_t *name, dns_db_t **dbp) +{ + isc_buffer_t b; + char namestr[DNS_NAME_MAXTEXT + 1]; + isc_result_t result; + dns_sdlzimplementation_t *imp; + + /* + * Perform checks to make sure data is as we expect it to be. + */ + REQUIRE(driverarg != NULL); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + imp = (dns_sdlzimplementation_t *) driverarg; + + /* Convert DNS name to ascii text */ + isc_buffer_init(&b, namestr, sizeof(namestr)); + result = dns_name_totext(name, ISC_TRUE, &b); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_putuint8(&b, 0); + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(namestr); + + /* Call SDLZ driver's find zone method */ + MAYBE_LOCK(imp); + result = imp->methods->findzone(imp->driverarg, dbdata, namestr); + MAYBE_UNLOCK(imp); + + /* + * if zone is supported build a 'bind' database driver + * structure to return + */ + if (result == ISC_R_SUCCESS) + result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name, + rdclass, dbp); + + return (result); +} + +static dns_dlzmethods_t sdlzmethods = { + dns_sdlzcreate, + dns_sdlzdestroy, + dns_sdlzfindzone, + dns_sdlzallowzonexfr +}; + +/* + * Public functions. + */ + +isc_result_t +dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl, + const char *data) +{ + dns_rdatalist_t *rdatalist; + dns_rdata_t *rdata; + dns_rdatatype_t typeval; + isc_consttextregion_t r; + isc_buffer_t b; + isc_buffer_t *rdatabuf; + isc_lex_t *lex; + isc_result_t result; + unsigned int size; + isc_mem_t *mctx; + dns_name_t *origin; + + REQUIRE(VALID_SDLZLOOKUP(lookup)); + REQUIRE(type != NULL); + REQUIRE(data != NULL); + + mctx = lookup->sdlz->common.mctx; + + r.base = type; + r.length = strlen(type); + result = dns_rdatatype_fromtext(&typeval, (void *) &r); + if (result != ISC_R_SUCCESS) + return (result); + + rdatalist = ISC_LIST_HEAD(lookup->lists); + while (rdatalist != NULL) { + if (rdatalist->type == typeval) + break; + rdatalist = ISC_LIST_NEXT(rdatalist, link); + } + + if (rdatalist == NULL) { + rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t)); + if (rdatalist == NULL) + return (ISC_R_NOMEMORY); + rdatalist->rdclass = lookup->sdlz->common.rdclass; + rdatalist->type = typeval; + rdatalist->covers = 0; + rdatalist->ttl = ttl; + ISC_LIST_INIT(rdatalist->rdata); + ISC_LINK_INIT(rdatalist, link); + ISC_LIST_APPEND(lookup->lists, rdatalist, link); + } else + if (rdatalist->ttl != ttl) + return (DNS_R_BADTTL); + + rdata = isc_mem_get(mctx, sizeof(dns_rdata_t)); + if (rdata == NULL) + return (ISC_R_NOMEMORY); + dns_rdata_init(rdata); + + if ((lookup->sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0) + origin = &lookup->sdlz->common.origin; + else + origin = dns_rootname; + + lex = NULL; + result = isc_lex_create(mctx, 64, &lex); + if (result != ISC_R_SUCCESS) + goto failure; + + size = initial_size(data); + do { + isc_buffer_init(&b, data, strlen(data)); + isc_buffer_add(&b, strlen(data)); + + result = isc_lex_openbuffer(lex, &b); + if (result != ISC_R_SUCCESS) + goto failure; + + rdatabuf = NULL; + result = isc_buffer_allocate(mctx, &rdatabuf, size); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_rdata_fromtext(rdata, rdatalist->rdclass, + rdatalist->type, lex, + origin, ISC_FALSE, + mctx, rdatabuf, + &lookup->callbacks); + if (result != ISC_R_SUCCESS) + isc_buffer_free(&rdatabuf); + size *= 2; + } while (result == ISC_R_NOSPACE); + + if (result != ISC_R_SUCCESS) + goto failure; + + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + ISC_LIST_APPEND(lookup->buffers, rdatabuf, link); + + if (lex != NULL) + isc_lex_destroy(&lex); + + return (ISC_R_SUCCESS); + + failure: + if (rdatabuf != NULL) + isc_buffer_free(&rdatabuf); + if (lex != NULL) + isc_lex_destroy(&lex); + isc_mem_put(mctx, rdata, sizeof(dns_rdata_t)); + + return (result); +} + +isc_result_t +dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name, + const char *type, dns_ttl_t ttl, const char *data) +{ + dns_name_t *newname, *origin; + dns_fixedname_t fnewname; + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)allnodes->common.db; + dns_sdlznode_t *sdlznode; + isc_mem_t *mctx = sdlz->common.mctx; + isc_buffer_t b; + isc_result_t result; + + dns_fixedname_init(&fnewname); + newname = dns_fixedname_name(&fnewname); + + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0) + origin = &sdlz->common.origin; + else + origin = dns_rootname; + isc_buffer_init(&b, name, strlen(name)); + isc_buffer_add(&b, strlen(name)); + + result = dns_name_fromtext(newname, &b, origin, ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + if (allnodes->common.relative_names) { + /* All names are relative to the root */ + unsigned int nlabels = dns_name_countlabels(newname); + dns_name_getlabelsequence(newname, 0, nlabels - 1, newname); + } + + sdlznode = ISC_LIST_HEAD(allnodes->nodelist); + if (sdlznode == NULL || !dns_name_equal(sdlznode->name, newname)) { + sdlznode = NULL; + result = createnode(sdlz, &sdlznode); + if (result != ISC_R_SUCCESS) + return (result); + sdlznode->name = isc_mem_get(mctx, sizeof(dns_name_t)); + if (sdlznode->name == NULL) { + destroynode(sdlznode); + return (ISC_R_NOMEMORY); + } + dns_name_init(sdlznode->name, NULL); + result = dns_name_dup(newname, mctx, sdlznode->name); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, sdlznode->name, sizeof(dns_name_t)); + destroynode(sdlznode); + return (result); + } + ISC_LIST_PREPEND(allnodes->nodelist, sdlznode, link); + if (allnodes->origin == NULL && + dns_name_equal(newname, &sdlz->common.origin)) + allnodes->origin = sdlznode; + } + return (dns_sdlz_putrr(sdlznode, type, ttl, data)); + +} + +isc_result_t +dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname, + isc_uint32_t serial) +{ + char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7]; + int n; + + REQUIRE(mname != NULL); + REQUIRE(rname != NULL); + + n = snprintf(str, sizeof str, "%s %s %u %u %u %u %u", + mname, rname, serial, + SDLZ_DEFAULT_REFRESH, SDLZ_DEFAULT_RETRY, + SDLZ_DEFAULT_EXPIRE, SDLZ_DEFAULT_MINIMUM); + if (n >= (int)sizeof(str) || n < 0) + return (ISC_R_NOSPACE); + return (dns_sdlz_putrr(lookup, "SOA", SDLZ_DEFAULT_TTL, str)); +} + +isc_result_t +dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods, + void *driverarg, unsigned int flags, isc_mem_t *mctx, + dns_sdlzimplementation_t **sdlzimp) +{ + + dns_sdlzimplementation_t *imp; + isc_result_t result; + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(drivername != NULL); + REQUIRE(methods != NULL); + REQUIRE(methods->findzone != NULL); + REQUIRE(methods->lookup != NULL); + REQUIRE(mctx != NULL); + REQUIRE(sdlzimp != NULL && *sdlzimp == NULL); + REQUIRE((flags & ~(DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE)) == 0); + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Registering SDLZ driver '%s'", drivername); + + /* + * Allocate memory for a sdlz_implementation object. Error if + * we cannot. + */ + imp = isc_mem_get(mctx, sizeof(dns_sdlzimplementation_t)); + if (imp == NULL) + return (ISC_R_NOMEMORY); + + /* Make sure memory region is set to all 0's */ + memset(imp, 0, sizeof(dns_sdlzimplementation_t)); + + /* Store the data passed into this method */ + imp->methods = methods; + imp->driverarg = driverarg; + imp->flags = flags; + imp->mctx = NULL; + + /* attach the new sdlz_implementation object to a memory context */ + isc_mem_attach(mctx, &imp->mctx); + + /* + * initialize the driver lock, error if we cannot + * (used if a driver does not support multiple threads) + */ + result = isc_mutex_init(&imp->driverlock); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + goto cleanup_mctx; + } + + imp->dlz_imp = NULL; + + /* + * register the DLZ driver. Pass in our "extra" sdlz information as + * a driverarg. (that's why we stored the passed in driver arg in our + * sdlz_implementation structure) Also, store the dlz_implementation + * structure in our sdlz_implementation. + */ + result = dns_dlzregister(drivername, &sdlzmethods, imp, mctx, + &imp->dlz_imp); + + /* if registration fails, cleanup and get outta here. */ + if (result != ISC_R_SUCCESS) + goto cleanup_mutex; + + *sdlzimp = imp; + + return (ISC_R_SUCCESS); + + cleanup_mutex: + /* destroy the driver lock, we don't need it anymore */ + DESTROYLOCK(&imp->driverlock); + + cleanup_mctx: + /* + * return the memory back to the available memory pool and + * remove it from the memory context. + */ + isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t)); + isc_mem_detach(&mctx); + return (result); +} + +void +dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp) { + dns_sdlzimplementation_t *imp; + isc_mem_t *mctx; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "Unregistering SDLZ driver."); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(sdlzimp != NULL && *sdlzimp != NULL); + + imp = *sdlzimp; + + /* Unregister the DLZ driver implementation */ + dns_dlzunregister(&imp->dlz_imp); + + /* destroy the driver lock, we don't need it anymore */ + DESTROYLOCK(&imp->driverlock); + + mctx = imp->mctx; + + /* + * return the memory back to the available memory pool and + * remove it from the memory context. + */ + isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t)); + isc_mem_detach(&mctx); + + *sdlzimp = NULL; +} diff --git a/lib/dns/view.c b/lib/dns/view.c index 00071c82752..e8e38c982a9 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.c,v 1.132 2005/07/12 01:00:16 marka Exp $ */ +/* $Id: view.c,v 1.133 2005/09/05 00:11:02 marka Exp $ */ /*! \file */ @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->acache = NULL; view->cache = NULL; view->cachedb = NULL; + view->dlzdatabase = NULL; view->hints = NULL; view->resolver = NULL; view->adb = NULL; @@ -269,6 +271,8 @@ destroy(dns_view_t *view) { isc_task_detach(&view->task); if (view->hints != NULL) dns_db_detach(&view->hints); + if (view->dlzdatabase != NULL) + dns_dlzdestroy(&view->dlzdatabase); if (view->cachedb != NULL) dns_db_detach(&view->cachedb); if (view->cache != NULL) diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def index 78d859c59aa..699bfdeb747 100644 --- a/lib/dns/win32/libdns.def +++ b/lib/dns/win32/libdns.def @@ -175,6 +175,13 @@ dns_dispatchmgr_destroy dns_dispatchmgr_getblackhole dns_dispatchmgr_setblackhole dns_dispatchmgr_setblackportlist +dns_dlzallowzonexfr +dns_dlzcreate +dns_dlzdestroy +dns_dlzfindzone +dns_dlzregister +dns_dlzstrtoargv +dns_dlzunregister dns_dnssec_findzonekeys dns_dnssec_findzonekeys2 dns_dnssec_keyfromrdata @@ -504,6 +511,11 @@ dns_sdb_putrr dns_sdb_putsoa dns_sdb_register dns_sdb_unregister +dns_sdlz_putnamedrr +dns_sdlz_putrr +dns_sdlz_putsoa +dns_sdlzregister +dns_sdlzunregister dns_secalg_fromtext dns_secalg_totext dns_secproto_fromtext diff --git a/lib/isc/unix/dir.c b/lib/isc/unix/dir.c index 557d46f68c7..0ab2ce15815 100644 --- a/lib/isc/unix/dir.c +++ b/lib/isc/unix/dir.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dir.c,v 1.22 2005/04/29 00:23:49 marka Exp $ */ +/* $Id: dir.c,v 1.23 2005/09/05 00:11:04 marka Exp $ */ /*! \file * \author Principal Authors: DCL */ @@ -57,11 +57,30 @@ isc_dir_init(isc_dir_t *dir) { */ isc_result_t isc_dir_open(isc_dir_t *dir, const char *dirname) { + char *p; isc_result_t result = ISC_R_SUCCESS; REQUIRE(VALID_DIR(dir)); REQUIRE(dirname != NULL); + /* + * Copy directory name. Need to have enough space for the name, + * a possible path separator, the wildcard, and the final NUL. + */ + if (strlen(dirname) + 3 > sizeof(dir->dirname)) + /* XXXDCL ? */ + return (ISC_R_NOSPACE); + strcpy(dir->dirname, dirname); + + /* + * Append path separator, if needed, and "*". + */ + p = dir->dirname + strlen(dir->dirname); + if (dir->dirname < p && *(p - 1) != '/') + *p++ = '/'; + *p++ = '*'; + *p++ = '\0'; + /* * Open stream. */ diff --git a/lib/isccfg/Makefile.in b/lib/isccfg/Makefile.in index feddcde69fc..55b8e9029a4 100644 --- a/lib/isccfg/Makefile.in +++ b/lib/isccfg/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.15 2005/01/12 01:56:12 marka Exp $ +# $Id: Makefile.in,v 1.16 2005/09/05 00:11:04 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -27,7 +27,7 @@ top_srcdir = @top_srcdir@ CINCLUDES = -I. ${DNS_INCLUDES} ${ISC_INCLUDES} ${ISCCFG_INCLUDES} -CDEFINES = +CDEFINES = @USE_DLZ@ CWARNINGS = ISCLIBS = ../../lib/isc/libisc.@A@ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 3613b33c9ab..23627f6b2d3 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.56 2005/08/24 23:54:04 marka Exp $ */ +/* $Id: namedconf.c,v 1.57 2005/09/05 00:11:05 marka Exp $ */ /*! \file */ @@ -108,6 +108,33 @@ static cfg_type_t cfg_type_view; static cfg_type_t cfg_type_viewopts; static cfg_type_t cfg_type_zone; static cfg_type_t cfg_type_zoneopts; +static cfg_type_t cfg_type_dynamically_loadable_zones; +static cfg_type_t cfg_type_dynamically_loadable_zones_opts; + +/* + * Clauses that can be found in a 'dynamically loadable zones' statement + */ +static cfg_clausedef_t +dynamically_loadable_zones_clauses[] = { + { "database", &cfg_type_astring, 0 }, + { NULL, NULL, 0 } +}; + +/* + * A dynamically loadable zones statement. + */ +static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "options", &cfg_type_dynamically_loadable_zones_opts, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_dynamically_loadable_zones = { + "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, + dynamically_loadable_zones_fields + }; + /*% tkey-dhkey */ @@ -567,6 +594,8 @@ static cfg_clausedef_t namedconf_or_view_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, + /* only 1 DLZ per view allowed */ + { "dlz", &cfg_type_dynamically_loadable_zones, 0 }, { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } @@ -873,6 +902,7 @@ view_clausesets[] = { namedconf_or_view_clauses, view_clauses, zone_clauses, + dynamically_loadable_zones_clauses, NULL }; static cfg_type_t cfg_type_viewopts = { @@ -887,8 +917,22 @@ zone_clausesets[] = { NULL }; static cfg_type_t cfg_type_zoneopts = { - "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; - + "zoneopts", cfg_parse_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, zone_clausesets }; + +/*% The "dynamically loadable zones" statement syntax. */ + +static cfg_clausedef_t * +dynamically_loadable_zones_clausesets[] = { + dynamically_loadable_zones_clauses, + NULL +}; +static cfg_type_t cfg_type_dynamically_loadable_zones_opts = { + "dynamically_loadable_zones_opts", cfg_parse_map, + cfg_print_map, cfg_doc_map, &cfg_rep_map, + dynamically_loadable_zones_clausesets +}; + /*% * Clauses that can be found within the 'key' statement. */ @@ -905,7 +949,9 @@ key_clausesets[] = { NULL }; static cfg_type_t cfg_type_key = { - "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; + "key", cfg_parse_named_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, key_clausesets +}; /*% diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index ac9dc214682..f60f5105a00 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: parser.c,v 1.118 2005/08/23 02:36:10 marka Exp $ */ +/* $Id: parser.c,v 1.119 2005/09/05 00:11:05 marka Exp $ */ /*! \file */ @@ -1087,7 +1087,6 @@ cfg_print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) { } } - isc_boolean_t cfg_obj_islist(cfg_obj_t *obj) { REQUIRE(obj != NULL);