1 /*	$NetBSD: sqlitedb.c,v 1.4 2014/12/10 04:37:57 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2007  Internet Software Consortium.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
11  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
12  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
13  * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
15  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
17  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id: sqlitedb.c,v 1.2 2011/10/11 00:09:02 each Exp  */
21 
22 #include <config.h>
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 
29 #include <sqlite3.h>
30 
31 #include <isc/mem.h>
32 #include <isc/print.h>
33 #include <isc/result.h>
34 #include <isc/util.h>
35 
36 #include <dns/sdb.h>
37 #include <dns/result.h>
38 
39 #include <named/globals.h>
40 
41 #include "sqlitedb.h"
42 
43 /*
44  * A simple database driver that interfaces to a SQLite database.
45  *
46  * The table must contain the fields "name", "rdtype", and "rdata", and
47  * is expected to contain a properly constructed zone.  The program "zonetodb"
48  * creates such a table.
49  */
50 
51 static dns_sdbimplementation_t *sqlitedb = NULL;
52 
53 typedef struct _dbinfo {
54     sqlite3 *db;
55     char *filename;
56     char *table;
57 } dbinfo_t;
58 
59 
60 static isc_result_t
61 db_connect(dbinfo_t *dbi)
62 {
63     if (sqlite3_open(dbi->filename, &dbi->db) == SQLITE_OK) {
64 	return (ISC_R_SUCCESS);
65     } else {
66 	/* a connection is returned even if the open fails */
67 	sqlite3_close(dbi->db);
68 	dbi->db = NULL;
69 	return (ISC_R_FAILURE);
70     }
71 }
72 
73 
74 typedef struct _lookup_parm_t {
75     int              i;
76     dns_sdblookup_t *lookup;
77     isc_result_t     result;
78 } lookup_parm_t;
79 
80 
81 static int
82 sqlitedb_lookup_cb(void *p, int cc, char **cv, char **cn)
83 {
84     lookup_parm_t *parm = p;
85     dns_ttl_t ttl;
86     char *endp;
87 
88     /* FIXME - check these(num/names); I'm assuming a mapping for now */
89     char *ttlstr = cv[0];
90     char *type   = cv[1];
91     char *data   = cv[2];
92 
93     UNUSED(cc);
94     UNUSED(cn);
95 
96     ttl = strtol(ttlstr, &endp, 10);
97     if (*endp) {
98 	parm->result = DNS_R_BADTTL;
99 	return 1;
100     }
101 
102     parm->result = dns_sdb_putrr(parm->lookup, type, ttl, data);
103 
104     if (parm->result != ISC_R_SUCCESS)
105 	return 1;
106 
107     (parm->i)++;
108 
109     return 0;
110 }
111 
112 
113 #ifdef DNS_CLIENTINFO_VERSION
114 static isc_result_t
115 sqlitedb_lookup(const char *zone, const char *name, void *dbdata,
116 		dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
117 		dns_clientinfo_t *clientinfo)
118 #else
119 static isc_result_t
120 sqlitedb_lookup(const char *zone, const char *name, void *dbdata,
121 		dns_sdblookup_t *lookup)
122 #endif /* DNS_CLIENTINFO_VERSION */
123 /*
124  * synchronous absolute name lookup
125  */
126 {
127     dbinfo_t *dbi = (dbinfo_t *) dbdata;
128     char *sql;
129     lookup_parm_t parm = { 0, lookup, ISC_R_SUCCESS };
130     char *errmsg = NULL;
131     int result;
132 
133     UNUSED(zone);
134 #ifdef DNS_CLIENTINFO_VERSION
135     UNUSED(methods);
136     UNUSED(clientinfo);
137 #endif /* DNS_CLIENTINFO_VERSION */
138 
139     sql = sqlite3_mprintf(
140 	"SELECT TTL,RDTYPE,RDATA FROM \"%q\" WHERE "
141 	"lower(NAME) = lower('%q')",
142 	dbi->table, name);
143 
144     result = sqlite3_exec(dbi->db, sql,
145 			  &sqlitedb_lookup_cb, &parm,
146 			  &errmsg);
147     sqlite3_free(sql);
148 
149     if (result != SQLITE_OK)
150 	return (ISC_R_FAILURE);
151     if (parm.i == 0)
152 	return (ISC_R_NOTFOUND);
153 
154     return (ISC_R_SUCCESS);
155 }
156 
157 
158 typedef struct _allnodes_parm_t {
159     int                i;
160     dns_sdballnodes_t *allnodes;
161     isc_result_t       result;
162 } allnodes_parm_t;
163 
164 
165 static int
166 sqlitedb_allnodes_cb(void *p, int cc, char **cv, char **cn)
167 {
168     allnodes_parm_t *parm = p;
169     dns_ttl_t ttl;
170     char *endp;
171 
172     /* FIXME - check these(num/names); I'm assuming a mapping for now */
173     char *ttlstr = cv[0];
174     char *name   = cv[1];
175     char *type   = cv[2];
176     char *data   = cv[3];
177 
178     UNUSED(cc);
179     UNUSED(cn);
180 
181     ttl = strtol(ttlstr, &endp, 10);
182     if (*endp) {
183 	parm->result = DNS_R_BADTTL;
184 	return 1;
185     }
186 
187     parm->result = dns_sdb_putnamedrr(parm->allnodes, name, type, ttl, data);
188 
189     if (parm->result != ISC_R_SUCCESS)
190 	return 1;
191 
192     (parm->i)++;
193 
194     return 0;
195 }
196 
197 
198 static isc_result_t
199 sqlitedb_allnodes(const char *zone,
200 		  void *dbdata,
201 		  dns_sdballnodes_t *allnodes)
202 {
203     dbinfo_t *dbi = (dbinfo_t *) dbdata;
204     char *sql;
205     allnodes_parm_t parm = { 0, allnodes, ISC_R_SUCCESS };
206     char *errmsg = NULL;
207     int result;
208 
209     UNUSED(zone);
210 
211     sql = sqlite3_mprintf(
212 	"SELECT TTL,NAME,RDTYPE,RDATA FROM \"%q\" ORDER BY NAME",
213 	dbi->table);
214 
215     result = sqlite3_exec(dbi->db, sql,
216 			  &sqlitedb_allnodes_cb, &parm,
217 			  &errmsg);
218     sqlite3_free(sql);
219 
220     if (result != SQLITE_OK)
221 	return (ISC_R_FAILURE);
222     if (parm.i == 0)
223 	return (ISC_R_NOTFOUND);
224 
225     return (ISC_R_SUCCESS);
226 }
227 
228 
229 static void
230 sqlitedb_destroy(const char *zone, void *driverdata, void **dbdata)
231 {
232     dbinfo_t *dbi = *dbdata;
233 
234     UNUSED(zone);
235     UNUSED(driverdata);
236 
237     if (dbi->db != NULL)
238 	sqlite3_close(dbi->db);
239     if (dbi->table != NULL)
240 	isc_mem_free(ns_g_mctx, dbi->table);
241     if (dbi->filename != NULL)
242 	isc_mem_free(ns_g_mctx, dbi->filename);
243 
244     isc_mem_put(ns_g_mctx, dbi, sizeof(dbinfo_t));
245 }
246 
247 
248 #define STRDUP_OR_FAIL(target, source)				\
249 	do {							\
250 		target = isc_mem_strdup(ns_g_mctx, source);	\
251 		if (target == NULL) {				\
252 			result = ISC_R_NOMEMORY;		\
253 			goto cleanup;				\
254 		}						\
255 	} while (/*CONSTCOND*/0);
256 
257 /*
258  * Create a connection to the database and save any necessary information
259  * in dbdata.
260  *
261  * argv[0] is the name of the database file
262  * argv[1] is the name of the table
263  */
264 static isc_result_t
265 sqlitedb_create(const char *zone,
266 		int argc, char **argv,
267 		void *driverdata, void **dbdata)
268 {
269     dbinfo_t *dbi;
270     isc_result_t result;
271 
272     UNUSED(zone);
273     UNUSED(driverdata);
274 
275     if (argc < 2)
276 	return (ISC_R_FAILURE);
277 
278     dbi = isc_mem_get(ns_g_mctx, sizeof(dbinfo_t));
279     if (dbi == NULL)
280 	return (ISC_R_NOMEMORY);
281     dbi->db       = NULL;
282     dbi->filename = NULL;
283     dbi->table    = NULL;
284 
285     STRDUP_OR_FAIL(dbi->filename, argv[0]);
286     STRDUP_OR_FAIL(dbi->table, argv[1]);
287 
288     result = db_connect(dbi);
289     if (result != ISC_R_SUCCESS)
290 	goto cleanup;
291 
292     *dbdata = dbi;
293     return (ISC_R_SUCCESS);
294 
295 cleanup:
296     sqlitedb_destroy(zone, driverdata, (void **)&dbi);
297     return (result);
298 }
299 
300 
301 /*
302  * Since the SQL database corresponds to a zone, the authority data should
303  * be returned by the lookup() function.  Therefore the authority() function
304  * is NULL.
305  */
306 static dns_sdbmethods_t sqlitedb_methods = {
307     sqlitedb_lookup,
308     NULL, /* authority */
309     sqlitedb_allnodes,
310     sqlitedb_create,
311     sqlitedb_destroy,
312     NULL /* lookup2 */
313 };
314 
315 
316 /*
317  * Wrapper around dns_sdb_register().
318  */
319 isc_result_t
320 sqlitedb_init(void)
321 {
322     unsigned int flags;
323     flags = 0;
324     return (dns_sdb_register("sqlite", &sqlitedb_methods, NULL, flags,
325 			     ns_g_mctx, &sqlitedb));
326 }
327 
328 
329 /*
330  * Wrapper around dns_sdb_unregister().
331  */
332 void
333 sqlitedb_clear(void)
334 {
335     if (sqlitedb != NULL)
336 	dns_sdb_unregister(&sqlitedb);
337 }
338