1/* 2 * Copyright (C) 2004, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000, 2001 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id$ */ 19 20/* 21 * A simple database driver that calls a Tcl procedure to define 22 * the contents of the DNS namespace. The procedure is loaded 23 * from the file lookup.tcl; look at the comments there for 24 * more information. 25 */ 26 27#include <config.h> 28 29#include <string.h> 30#include <stdlib.h> 31#include <unistd.h> 32#include <sys/stat.h> 33 34#include <isc/mem.h> 35#include <isc/print.h> 36#include <isc/result.h> 37#include <isc/util.h> 38 39#include <dns/log.h> 40#include <dns/sdb.h> 41 42#include <named/globals.h> 43 44#include <tcl.h> 45 46#include <tcldb.h> 47 48#define CHECK(op) \ 49 do { result = (op); \ 50 if (result != ISC_R_SUCCESS) return (result); \ 51 } while (0) 52 53typedef struct tcldb_driver { 54 isc_mem_t *mctx; 55 Tcl_Interp *interp; 56} tcldb_driver_t; 57 58static tcldb_driver_t *the_driver = NULL; 59 60static dns_sdbimplementation_t *tcldb = NULL; 61 62static isc_result_t 63tcldb_driver_create(isc_mem_t *mctx, tcldb_driver_t **driverp) { 64 int tclres; 65 isc_result_t result = ISC_R_SUCCESS; 66 tcldb_driver_t *driver = isc_mem_get(mctx, sizeof(tcldb_driver_t)); 67 if (driver == NULL) 68 return (ISC_R_NOMEMORY); 69 driver->mctx = mctx; 70 driver->interp = Tcl_CreateInterp(); 71 72 tclres = Tcl_EvalFile(driver->interp, (char *) "lookup.tcl"); 73 if (tclres != TCL_OK) { 74 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 75 DNS_LOGMODULE_SDB, ISC_LOG_ERROR, 76 "initializing tcldb: " 77 "loading 'lookup.tcl' failed: %s", 78 driver->interp->result); 79 result = ISC_R_FAILURE; 80 goto cleanup; 81 } 82 *driverp = driver; 83 return (ISC_R_SUCCESS); 84 85 cleanup: 86 isc_mem_put(mctx, driver, sizeof(tcldb_driver_t)); 87 return (result); 88 89} 90 91static void 92tcldb_driver_destroy(tcldb_driver_t **driverp) { 93 tcldb_driver_t *driver = *driverp; 94 Tcl_DeleteInterp(driver->interp); 95 isc_mem_put(driver->mctx, driver, sizeof(tcldb_driver_t)); 96} 97 98/* 99 * Perform a lookup, by invoking the Tcl procedure "lookup". 100 */ 101static isc_result_t 102tcldb_lookup(const char *zone, const char *name, void *dbdata, 103 dns_sdblookup_t *lookup) 104{ 105 isc_result_t result = ISC_R_SUCCESS; 106 int tclres; 107 int rrc; /* RR count */ 108 char **rrv; /* RR vector */ 109 int i; 110 char *cmdv[3]; 111 char *cmd; 112 113 tcldb_driver_t *driver = (tcldb_driver_t *) dbdata; 114 115 cmdv[0] = "lookup"; 116 cmdv[1] = zone; 117 cmdv[2] = name; 118 cmd = Tcl_Merge(3, cmdv); 119 tclres = Tcl_Eval(driver->interp, cmd); 120 Tcl_Free(cmd); 121 122 if (tclres != TCL_OK) { 123 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 124 DNS_LOGMODULE_SDB, ISC_LOG_ERROR, 125 "zone '%s': tcl lookup function failed: %s", 126 zone, driver->interp->result); 127 return (ISC_R_FAILURE); 128 } 129 130 if (strcmp(driver->interp->result, "NXDOMAIN") == 0) { 131 result = ISC_R_NOTFOUND; 132 goto fail; 133 } 134 135 tclres = Tcl_SplitList(driver->interp, driver->interp->result, 136 &rrc, &rrv); 137 if (tclres != TCL_OK) 138 goto malformed; 139 140 for (i = 0; i < rrc; i++) { 141 isc_result_t tmpres; 142 int fieldc; /* Field count */ 143 char **fieldv; /* Field vector */ 144 tclres = Tcl_SplitList(driver->interp, rrv[i], 145 &fieldc, &fieldv); 146 if (tclres != TCL_OK) { 147 tmpres = ISC_R_FAILURE; 148 goto failrr; 149 } 150 if (fieldc != 3) 151 goto malformed; 152 tmpres = dns_sdb_putrr(lookup, fieldv[0], atoi(fieldv[1]), 153 fieldv[2]); 154 Tcl_Free((char *) fieldv); 155 failrr: 156 if (tmpres != ISC_R_SUCCESS) 157 result = tmpres; 158 } 159 Tcl_Free((char *) rrv); 160 if (result == ISC_R_SUCCESS) 161 return (result); 162 163 malformed: 164 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 165 DNS_LOGMODULE_SDB, ISC_LOG_ERROR, 166 "zone '%s': " 167 "malformed return value from tcl lookup function: %s", 168 zone, driver->interp->result); 169 result = ISC_R_FAILURE; 170 fail: 171 return (result); 172} 173 174/* 175 * Set up per-zone state. In our case, the database arguments of the 176 * zone are collected into a Tcl list and assigned to an element of 177 * the global array "dbargs". 178 */ 179static isc_result_t 180tcldb_create(const char *zone, int argc, char **argv, 181 void *driverdata, void **dbdata) 182{ 183 tcldb_driver_t *driver = (tcldb_driver_t *) driverdata; 184 185 char *list = Tcl_Merge(argc, argv); 186 187 Tcl_SetVar2(driver->interp, (char *) "dbargs", (char *) zone, list, 0); 188 189 Tcl_Free(list); 190 191 *dbdata = driverdata; 192 193 return (ISC_R_SUCCESS); 194} 195 196/* 197 * This driver does not support zone transfer, so allnodes() is NULL. 198 */ 199static dns_sdbmethods_t tcldb_methods = { 200 tcldb_lookup, 201 NULL, /* authority */ 202 NULL, /* allnodes */ 203 tcldb_create, 204 NULL /* destroy */ 205}; 206 207/* 208 * Initialize the tcldb driver. 209 */ 210isc_result_t 211tcldb_init(void) { 212 isc_result_t result; 213 int flags = DNS_SDBFLAG_RELATIVEOWNER | DNS_SDBFLAG_RELATIVERDATA; 214 215 result = tcldb_driver_create(ns_g_mctx, &the_driver); 216 if (result != ISC_R_SUCCESS) 217 return (result); 218 219 return (dns_sdb_register("tcl", &tcldb_methods, the_driver, flags, 220 ns_g_mctx, &tcldb)); 221} 222 223/* 224 * Wrapper around dns_sdb_unregister(). 225 */ 226void 227tcldb_clear(void) { 228 if (tcldb != NULL) 229 dns_sdb_unregister(&tcldb); 230 if (the_driver != NULL) 231 tcldb_driver_destroy(&the_driver); 232} 233