1/*	$NetBSD$	*/
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
51static dns_sdbimplementation_t *sqlitedb = NULL;
52
53typedef struct _dbinfo {
54    sqlite3 *db;
55    char *filename;
56    char *table;
57} dbinfo_t;
58
59
60static isc_result_t
61db_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
74typedef struct _lookup_parm_t {
75    int              i;
76    dns_sdblookup_t *lookup;
77    isc_result_t     result;
78} lookup_parm_t;
79
80
81static int
82sqlitedb_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
114static isc_result_t
115sqlitedb_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
119static isc_result_t
120sqlitedb_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
158typedef struct _allnodes_parm_t {
159    int                i;
160    dns_sdballnodes_t *allnodes;
161    isc_result_t       result;
162} allnodes_parm_t;
163
164
165static int
166sqlitedb_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
198static isc_result_t
199sqlitedb_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
229static void
230sqlitedb_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 */
264static isc_result_t
265sqlitedb_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
295cleanup:
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 */
306static dns_sdbmethods_t sqlitedb_methods = {
307    sqlitedb_lookup,
308    NULL, /* authority */
309    sqlitedb_allnodes,
310    sqlitedb_create,
311    sqlitedb_destroy
312};
313
314
315/*
316 * Wrapper around dns_sdb_register().
317 */
318isc_result_t
319sqlitedb_init(void)
320{
321    unsigned int flags;
322    flags = 0;
323    return (dns_sdb_register("sqlite", &sqlitedb_methods, NULL, flags,
324			     ns_g_mctx, &sqlitedb));
325}
326
327
328/*
329 * Wrapper around dns_sdb_unregister().
330 */
331void
332sqlitedb_clear(void)
333{
334    if (sqlitedb != NULL)
335	dns_sdb_unregister(&sqlitedb);
336}
337