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