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