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