Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / dlz / drivers / dlz_mysql_driver.c
blobcb2eb11df37fd261aeb660a69e762634abb99eae
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the
8 * above copyright notice and this permission notice appear in all
9 * copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
12 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
17 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
18 * USE OR PERFORMANCE OF THIS SOFTWARE.
20 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21 * conceived and contributed by Rob Butler.
23 * Permission to use, copy, modify, and distribute this software for any
24 * purpose with or without fee is hereby granted, provided that the
25 * above copyright notice and this permission notice appear in all
26 * copies.
28 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
29 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
31 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
34 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
35 * USE OR PERFORMANCE OF THIS SOFTWARE.
39 * Copyright (C) 1999-2001 Internet Software Consortium.
41 * Permission to use, copy, modify, and distribute this software for any
42 * purpose with or without fee is hereby granted, provided that the above
43 * copyright notice and this permission notice appear in all copies.
45 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
46 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
48 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
49 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
50 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
51 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
52 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
55 #ifdef DLZ_MYSQL
57 #include <config.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <stdlib.h>
62 #include <dns/log.h>
63 #include <dns/sdlz.h>
64 #include <dns/result.h>
66 #include <isc/mem.h>
67 #include <isc/platform.h>
68 #include <isc/print.h>
69 #include <isc/result.h>
70 #include <isc/string.h>
71 #include <isc/util.h>
73 #include <named/globals.h>
75 #include <dlz/sdlz_helper.h>
76 #include <dlz/dlz_mysql_driver.h>
78 #include <mysql.h>
80 static dns_sdlzimplementation_t *dlz_mysql = NULL;
82 #define dbc_search_limit 30
83 #define ALLNODES 1
84 #define ALLOWXFR 2
85 #define AUTHORITY 3
86 #define FINDZONE 4
87 #define COUNTZONE 5
88 #define LOOKUP 6
90 #define safeGet(in) in == NULL ? "" : in
93 * Private methods
96 /*%
97 * Allocates memory for a new string, and then constructs the new
98 * string by "escaping" the input string. The new string is
99 * safe to be used in queries. This is necessary because we cannot
100 * be sure of what types of strings are passed to us, and we don't
101 * want special characters in the string causing problems.
104 static char *
105 mysqldrv_escape_string(MYSQL *mysql, const char *instr) {
107 char *outstr;
108 unsigned int len;
110 if (instr == NULL)
111 return NULL;
113 len = strlen(instr);
115 outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
116 if (outstr == NULL)
117 return NULL;
119 mysql_real_escape_string(mysql, outstr, instr, len);
121 return outstr;
125 * This function is the real core of the driver. Zone, record
126 * and client strings are passed in (or NULL is passed if the
127 * string is not available). The type of query we want to run
128 * is indicated by the query flag, and the dbdata object is passed
129 * passed in to. dbdata really holds a single database instance.
130 * The function will construct and run the query, hopefully getting
131 * a result set.
134 static isc_result_t
135 mysql_get_resultset(const char *zone, const char *record,
136 const char *client, unsigned int query,
137 void *dbdata, MYSQL_RES **rs)
139 isc_result_t result;
140 dbinstance_t *dbi = NULL;
141 char *querystring = NULL;
142 unsigned int i = 0;
143 unsigned int j = 0;
144 int qres = 0;
146 if (query != COUNTZONE)
147 REQUIRE(*rs == NULL);
148 else
149 REQUIRE(rs == NULL);
151 /* get db instance / connection */
152 dbi = (dbinstance_t *) dbdata;
154 /* if DBI is null, can't do anything else */
155 if (dbi == NULL) {
156 result = ISC_R_FAILURE;
157 goto cleanup;
160 /* what type of query are we going to run? */
161 switch(query) {
162 case ALLNODES:
164 * if the query was not passed in from the config file
165 * then we can't run it. return not_implemented, so
166 * it's like the code for that operation was never
167 * built into the driver.... AHHH flexibility!!!
169 if (dbi->allnodes_q == NULL) {
170 result = ISC_R_NOTIMPLEMENTED;
171 goto cleanup;
173 break;
174 case ALLOWXFR:
175 /* same as comments as ALLNODES */
176 if (dbi->allowxfr_q == NULL) {
177 result = ISC_R_NOTIMPLEMENTED;
178 goto cleanup;
180 break;
181 case AUTHORITY:
182 /* same as comments as ALLNODES */
183 if (dbi->authority_q == NULL) {
184 result = ISC_R_NOTIMPLEMENTED;
185 goto cleanup;
187 break;
188 case FINDZONE:
189 /* this is required. It's the whole point of DLZ! */
190 if (dbi->findzone_q == NULL) {
191 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
192 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
193 "No query specified for findzone. "
194 "Findzone requires a query");
195 result = ISC_R_FAILURE;
196 goto cleanup;
198 break;
199 case COUNTZONE:
200 /* same as comments as ALLNODES */
201 if (dbi->countzone_q == NULL) {
202 result = ISC_R_NOTIMPLEMENTED;
203 goto cleanup;
205 break;
206 case LOOKUP:
207 /* this is required. It's also a major point of DLZ! */
208 if (dbi->lookup_q == NULL) {
209 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
210 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
211 "No query specified for lookup. "
212 "Lookup requires a query");
213 result = ISC_R_FAILURE;
214 goto cleanup;
216 break;
217 default:
219 * this should never happen. If it does, the code is
220 * screwed up!
222 UNEXPECTED_ERROR(__FILE__, __LINE__,
223 "Incorrect query flag passed to "
224 "mysql_get_resultset");
225 result = ISC_R_UNEXPECTED;
226 goto cleanup;
231 * was a zone string passed? If so, make it safe for use in
232 * queries.
234 if (zone != NULL) {
235 dbi->zone = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
236 zone);
237 if (dbi->zone == NULL) {
238 result = ISC_R_NOMEMORY;
239 goto cleanup;
241 } else { /* no string passed, set the string pointer to NULL */
242 dbi->zone = NULL;
246 * was a record string passed? If so, make it safe for use in
247 * queries.
249 if (record != NULL) {
250 dbi->record = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
251 record);
252 if (dbi->record == NULL) {
253 result = ISC_R_NOMEMORY;
254 goto cleanup;
256 } else { /* no string passed, set the string pointer to NULL */
257 dbi->record = NULL;
261 * was a client string passed? If so, make it safe for use in
262 * queries.
264 if (client != NULL) {
265 dbi->client = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
266 client);
267 if (dbi->client == NULL) {
268 result = ISC_R_NOMEMORY;
269 goto cleanup;
271 } else { /* no string passed, set the string pointer to NULL */
272 dbi->client = NULL;
276 * what type of query are we going to run? this time we build
277 * the actual query to run.
279 switch(query) {
280 case ALLNODES:
281 querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
282 break;
283 case ALLOWXFR:
284 querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
285 break;
286 case AUTHORITY:
287 querystring = build_querystring(ns_g_mctx, dbi->authority_q);
288 break;
289 case FINDZONE:
290 querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
291 break;
292 case COUNTZONE:
293 querystring = build_querystring(ns_g_mctx, dbi->countzone_q);
294 break;
295 case LOOKUP:
296 querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
297 break;
298 default:
300 * this should never happen. If it does, the code is
301 * screwed up!
303 UNEXPECTED_ERROR(__FILE__, __LINE__,
304 "Incorrect query flag passed to "
305 "mysql_get_resultset");
306 result = ISC_R_UNEXPECTED;
307 goto cleanup;
310 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
311 if (querystring == NULL) {
312 result = ISC_R_NOMEMORY;
313 goto cleanup;
317 * output the full query string during debug so we can see
318 * what lame error the query has.
320 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
321 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
322 "\nQuery String: %s\n", querystring);
324 /* attempt query up to 3 times. */
325 for (i=0; i < 3; i++) {
326 qres = mysql_query((MYSQL *) dbi->dbconn, querystring);
327 if (qres == 0)
328 break;
329 for (j=0; mysql_ping((MYSQL *) dbi->dbconn) != 0 && j < 4; j++)
333 if (qres == 0) {
334 result = ISC_R_SUCCESS;
335 if (query != COUNTZONE) {
336 *rs = mysql_store_result((MYSQL *) dbi->dbconn);
337 if (*rs == NULL)
338 result = ISC_R_FAILURE;
340 } else {
341 result = ISC_R_FAILURE;
345 cleanup:
346 /* it's always good to cleanup after yourself */
348 /* if we couldn't even get DBI, just return NULL */
349 if (dbi == NULL)
350 return ISC_R_FAILURE;
352 /* free dbi->zone string */
353 if (dbi->zone != NULL)
354 isc_mem_free(ns_g_mctx, dbi->zone);
356 /* free dbi->record string */
357 if (dbi->record != NULL)
358 isc_mem_free(ns_g_mctx, dbi->record);
360 /* free dbi->client string */
361 if (dbi->client != NULL)
362 isc_mem_free(ns_g_mctx, dbi->client);
364 /* release query string */
365 if (querystring != NULL)
366 isc_mem_free(ns_g_mctx, querystring);
368 /* return result */
369 return result;
373 * The processing of result sets for lookup and authority are
374 * exactly the same. So that functionality has been moved
375 * into this function to minimize code.
378 static isc_result_t
379 mysql_process_rs(dns_sdlzlookup_t *lookup, MYSQL_RES *rs)
381 isc_result_t result = ISC_R_NOTFOUND;
382 MYSQL_ROW row;
383 unsigned int fields;
384 unsigned int j;
385 unsigned int len;
386 char *tmpString;
387 char *endp;
388 int ttl;
390 row = mysql_fetch_row(rs); /* get a row from the result set */
391 fields = mysql_num_fields(rs); /* how many columns in result set */
392 while (row != NULL) {
393 switch(fields) {
394 case 1:
396 * one column in rs, it's the data field. use
397 * default type of A record, and default TTL
398 * of 86400
400 result = dns_sdlz_putrr(lookup, "a", 86400,
401 safeGet(row[0]));
402 break;
403 case 2:
405 * two columns, data field, and data type.
406 * use default TTL of 86400.
408 result = dns_sdlz_putrr(lookup, safeGet(row[0]), 86400,
409 safeGet(row[1]));
410 break;
411 case 3:
413 * three columns, all data no defaults.
414 * convert text to int, make sure it worked
415 * right.
417 ttl = strtol(safeGet(row[0]), &endp, 10);
418 if (*endp != '\0' || ttl < 0) {
419 isc_log_write(dns_lctx,
420 DNS_LOGCATEGORY_DATABASE,
421 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
422 "mysql driver ttl must be "
423 "a postive number");
425 result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl,
426 safeGet(row[2]));
427 break;
428 default:
430 * more than 3 fields, concatenate the last
431 * ones together. figure out how long to make
432 * string.
434 for (j=2, len=0; j < fields; j++) {
435 len += strlen(safeGet(row[j])) + 1;
438 * allocate string memory, allow for NULL to
439 * term string
441 tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
442 if (tmpString == NULL) {
443 /* major bummer, need more ram */
444 isc_log_write(dns_lctx,
445 DNS_LOGCATEGORY_DATABASE,
446 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
447 "mysql driver unable "
448 "to allocate memory for "
449 "temporary string");
450 mysql_free_result(rs);
451 return (ISC_R_FAILURE); /* Yeah, I'd say! */
453 /* copy field to tmpString */
454 strcpy(tmpString, safeGet(row[2]));
458 * concat the rest of fields together, space
459 * between each one.
461 for (j=3; j < fields; j++) {
462 strcat(tmpString, " ");
463 strcat(tmpString, safeGet(row[j]));
465 /* convert text to int, make sure it worked right */
466 ttl = strtol(safeGet(row[0]), &endp, 10);
467 if (*endp != '\0' || ttl < 0) {
468 isc_log_write(dns_lctx,
469 DNS_LOGCATEGORY_DATABASE,
470 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
471 "mysql driver ttl must be "
472 "a postive number");
474 /* ok, now tell Bind about it. */
475 result = dns_sdlz_putrr(lookup, safeGet(row[1]),
476 ttl, tmpString);
477 /* done, get rid of this thing. */
478 isc_mem_free(ns_g_mctx, tmpString);
480 /* I sure hope we were successful */
481 if (result != ISC_R_SUCCESS) {
482 /* nope, get rid of the Result set, and log a msg */
483 mysql_free_result(rs);
484 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
485 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
486 "dns_sdlz_putrr returned error. "
487 "Error code was: %s",
488 isc_result_totext(result));
489 return (ISC_R_FAILURE);
491 row = mysql_fetch_row(rs); /* get next row */
494 /* free result set memory */
495 mysql_free_result(rs);
497 /* return result code */
498 return result;
502 * SDLZ interface methods
505 /*% determine if the zone is supported by (in) the database */
507 static isc_result_t
508 mysql_findzone(void *driverarg, void *dbdata, const char *name)
510 isc_result_t result;
511 MYSQL_RES *rs = NULL;
512 my_ulonglong rows;
514 UNUSED(driverarg);
516 /* run the query and get the result set from the database. */
517 result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs);
518 /* if we didn't get a result set, log an err msg. */
519 if (result != ISC_R_SUCCESS || rs == NULL) {
520 if (rs != NULL)
521 mysql_free_result(rs);
522 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
523 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
524 "mysql driver unable to return "
525 "result set for findzone query");
526 return (ISC_R_FAILURE);
528 /* count how many rows in result set */
529 rows = mysql_num_rows(rs);
530 /* get rid of result set, we are done with it. */
531 mysql_free_result(rs);
533 /* if we returned any rows, zone is supported. */
534 if (rows > 0) {
535 mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL);
536 return (ISC_R_SUCCESS);
539 /* no rows returned, zone is not supported. */
540 return (ISC_R_NOTFOUND);
543 /*% Determine if the client is allowed to perform a zone transfer */
544 static isc_result_t
545 mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name,
546 const char *client)
548 isc_result_t result;
549 MYSQL_RES *rs = NULL;
550 my_ulonglong rows;
552 UNUSED(driverarg);
554 /* first check if the zone is supported by the database. */
555 result = mysql_findzone(driverarg, dbdata, name);
556 if (result != ISC_R_SUCCESS)
557 return (ISC_R_NOTFOUND);
560 * if we get to this point we know the zone is supported by
561 * the database the only questions now are is the zone
562 * transfer is allowed for this client and did the config file
563 * have an allow zone xfr query.
565 * Run our query, and get a result set from the database.
567 result = mysql_get_resultset(name, NULL, client, ALLOWXFR,
568 dbdata, &rs);
569 /* if we get "not implemented", send it along. */
570 if (result == ISC_R_NOTIMPLEMENTED)
571 return result;
572 /* if we didn't get a result set, log an err msg. */
573 if (result != ISC_R_SUCCESS || rs == NULL) {
574 if (rs != NULL)
575 mysql_free_result(rs);
576 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
577 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
578 "mysql driver unable to return "
579 "result set for allow xfr query");
580 return (ISC_R_FAILURE);
582 /* count how many rows in result set */
583 rows = mysql_num_rows(rs);
584 /* get rid of result set, we are done with it. */
585 mysql_free_result(rs);
587 /* if we returned any rows, zone xfr is allowed. */
588 if (rows > 0)
589 return (ISC_R_SUCCESS);
591 /* no rows returned, zone xfr not allowed */
592 return (ISC_R_NOPERM);
596 * If the client is allowed to perform a zone transfer, the next order of
597 * business is to get all the nodes in the zone, so bind can respond to the
598 * query.
600 static isc_result_t
601 mysql_allnodes(const char *zone, void *driverarg, void *dbdata,
602 dns_sdlzallnodes_t *allnodes)
604 isc_result_t result;
605 MYSQL_RES *rs = NULL;
606 MYSQL_ROW row;
607 unsigned int fields;
608 unsigned int j;
609 unsigned int len;
610 char *tmpString;
611 char *endp;
612 int ttl;
614 UNUSED(driverarg);
616 /* run the query and get the result set from the database. */
617 result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs);
618 /* if we get "not implemented", send it along */
619 if (result == ISC_R_NOTIMPLEMENTED)
620 return result;
621 /* if we didn't get a result set, log an err msg. */
622 if (result != ISC_R_SUCCESS) {
623 if (rs != NULL)
624 mysql_free_result(rs);
625 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
626 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
627 "mysql driver unable to return "
628 "result set for all nodes query");
629 return (ISC_R_FAILURE);
632 result = ISC_R_NOTFOUND;
634 row = mysql_fetch_row(rs); /* get a row from the result set */
635 fields = mysql_num_fields(rs); /* how many columns in result set */
636 while (row != NULL) {
637 if (fields < 4) { /* gotta have at least 4 columns */
638 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
639 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
640 "mysql driver too few fields returned "
641 "by all nodes query");
643 /* convert text to int, make sure it worked right */
644 ttl = strtol(safeGet(row[0]), &endp, 10);
645 if (*endp != '\0' || ttl < 0) {
646 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
647 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
648 "mysql driver ttl must be "
649 "a postive number");
651 if (fields == 4) {
652 /* tell Bind about it. */
653 result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
654 safeGet(row[1]), ttl,
655 safeGet(row[3]));
656 } else {
658 * more than 4 fields, concatenate the last
659 * ones together. figure out how long to make
660 * string.
662 for (j=3, len=0; j < fields; j++) {
663 len += strlen(safeGet(row[j])) + 1;
665 /* allocate memory, allow for NULL to term string */
666 tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
667 if (tmpString == NULL) { /* we need more ram. */
668 isc_log_write(dns_lctx,
669 DNS_LOGCATEGORY_DATABASE,
670 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
671 "mysql driver unable "
672 "to allocate memory for "
673 "temporary string");
674 mysql_free_result(rs);
675 return (ISC_R_FAILURE);
677 /* copy this field to tmpString */
678 strcpy(tmpString, safeGet(row[3]));
679 /* concatonate the rest, with spaces between */
680 for (j=4; j < fields; j++) {
681 strcat(tmpString, " ");
682 strcat(tmpString, safeGet(row[j]));
684 /* tell Bind about it. */
685 result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
686 safeGet(row[1]),
687 ttl, tmpString);
688 isc_mem_free(ns_g_mctx, tmpString);
690 /* if we weren't successful, log err msg */
691 if (result != ISC_R_SUCCESS) {
692 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
693 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
694 "dns_sdlz_putnamedrr returned error. "
695 "Error code was: %s",
696 isc_result_totext(result));
697 result = ISC_R_FAILURE;
698 break;
700 /* get next row from the result set */
701 row = mysql_fetch_row(rs);
704 /* free result set memory */
705 mysql_free_result(rs);
707 return result;
710 /*% if the lookup function does not return SOA or NS records for the zone,
711 * use this function to get that information for Bind.
714 static isc_result_t
715 mysql_authority(const char *zone, void *driverarg, void *dbdata,
716 dns_sdlzlookup_t *lookup)
718 isc_result_t result;
719 MYSQL_RES *rs = NULL;
721 UNUSED(driverarg);
723 /* run the query and get the result set from the database. */
724 result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs);
725 /* if we get "not implemented", send it along */
726 if (result == ISC_R_NOTIMPLEMENTED)
727 return result;
728 /* if we didn't get a result set, log an err msg. */
729 if (result != ISC_R_SUCCESS) {
730 if (rs != NULL)
731 mysql_free_result(rs);
732 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
733 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
734 "mysql driver unable to return "
735 "result set for authority query");
736 return (ISC_R_FAILURE);
739 * lookup and authority result sets are processed in the same
740 * manner mysql_process_rs does the job for both functions.
742 return mysql_process_rs(lookup, rs);
745 /*% if zone is supported, lookup up a (or multiple) record(s) in it */
746 static isc_result_t
747 mysql_lookup(const char *zone, const char *name, void *driverarg,
748 void *dbdata, dns_sdlzlookup_t *lookup)
750 isc_result_t result;
751 MYSQL_RES *rs = NULL;
753 UNUSED(driverarg);
755 /* run the query and get the result set from the database. */
756 result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
757 /* if we didn't get a result set, log an err msg. */
758 if (result != ISC_R_SUCCESS) {
759 if (rs != NULL)
760 mysql_free_result(rs);
761 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
762 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
763 "mysql driver unable to return "
764 "result set for lookup query");
765 return (ISC_R_FAILURE);
768 * lookup and authority result sets are processed in the same manner
769 * mysql_process_rs does the job for both functions.
771 return mysql_process_rs(lookup, rs);
775 * create an instance of the driver. Remember, only 1 copy of the driver's
776 * code is ever loaded, the driver has to remember which context it's
777 * operating in. This is done via use of the dbdata argument which is
778 * passed into all query functions.
780 static isc_result_t
781 mysql_create(const char *dlzname, unsigned int argc, char *argv[],
782 void *driverarg, void **dbdata)
784 isc_result_t result;
785 dbinstance_t *dbi = NULL;
786 char *tmp = NULL;
787 char *dbname = NULL;
788 char *host = NULL;
789 char *user = NULL;
790 char *pass = NULL;
791 char *socket = NULL;
792 int port;
793 MYSQL *dbc;
794 char *endp;
795 int j;
796 unsigned int flags = 0;
797 #if MYSQL_VERSION_ID >= 50000
798 my_bool auto_reconnect = 1;
799 #endif
801 UNUSED(driverarg);
802 UNUSED(dlzname);
804 /* verify we have at least 4 arg's passed to the driver */
805 if (argc < 4) {
806 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
807 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
808 "mysql driver requires "
809 "at least 4 command line args.");
810 return (ISC_R_FAILURE);
813 /* no more than 8 arg's should be passed to the driver */
814 if (argc > 8) {
815 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
816 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
817 "mysql driver cannot accept "
818 "more than 7 command line args.");
819 return (ISC_R_FAILURE);
822 /* parse connection string and get paramters. */
824 /* get db name - required */
825 dbname = getParameterValue(argv[1], "dbname=");
826 if (dbname == NULL) {
827 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
828 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
829 "mysql driver requires a dbname parameter.");
830 result = ISC_R_FAILURE;
831 goto full_cleanup;
834 /* get db port. Not required, but must be > 0 if specified */
835 tmp = getParameterValue(argv[1], "port=");
836 if (tmp == NULL) {
837 port = 0;
838 } else {
839 port = strtol(tmp, &endp, 10);
840 if (*endp != '\0' || port < 0) {
841 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
842 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
843 "Mysql driver port "
844 "must be a positive number.");
845 isc_mem_free(ns_g_mctx, tmp);
846 result = ISC_R_FAILURE;
847 goto full_cleanup;
849 isc_mem_free(ns_g_mctx, tmp);
852 /* how many queries were passed in from config file? */
853 switch(argc) {
854 case 4:
855 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL,
856 argv[2], argv[3], NULL, &dbi);
857 break;
858 case 5:
859 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[4],
860 argv[2], argv[3], NULL, &dbi);
861 break;
862 case 6:
863 result = build_sqldbinstance(ns_g_mctx, argv[5], NULL, argv[4],
864 argv[2], argv[3], NULL, &dbi);
865 break;
866 case 7:
867 result = build_sqldbinstance(ns_g_mctx, argv[5],
868 argv[6], argv[4],
869 argv[2], argv[3], NULL, &dbi);
870 break;
871 case 8:
872 result = build_sqldbinstance(ns_g_mctx, argv[5],
873 argv[6], argv[4],
874 argv[2], argv[3], argv[7], &dbi);
875 break;
876 default:
877 /* not really needed, should shut up compiler. */
878 result = ISC_R_FAILURE;
881 /* unsuccessful?, log err msg and cleanup. */
882 if (result != ISC_R_SUCCESS) {
883 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
884 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
885 "mysql driver could not create "
886 "database instance object.");
887 result = ISC_R_FAILURE;
888 goto full_cleanup;
891 /* create and set db connection */
892 dbi->dbconn = mysql_init(NULL);
894 /* if db connection cannot be created, log err msg and cleanup. */
895 if (dbi->dbconn == NULL) {
896 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
897 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
898 "mysql driver could not allocate "
899 "memory for database connection");
900 result = ISC_R_FAILURE;
901 goto full_cleanup;
904 tmp = getParameterValue(argv[1], "compress=");
905 if (tmp != NULL) {
906 if (strcasecmp(tmp, "true") == 0)
907 flags = CLIENT_COMPRESS;
908 isc_mem_free(ns_g_mctx, tmp);
911 tmp = getParameterValue(argv[1], "ssl=");
912 if (tmp != NULL) {
913 if (strcasecmp(tmp, "true") == 0)
914 flags = flags | CLIENT_SSL;
915 isc_mem_free(ns_g_mctx, tmp);
918 tmp = getParameterValue(argv[1], "space=");
919 if (tmp != NULL) {
920 if (strcasecmp(tmp, "ignore") == 0)
921 flags = flags | CLIENT_IGNORE_SPACE;
922 isc_mem_free(ns_g_mctx, tmp);
925 dbc = NULL;
926 host = getParameterValue(argv[1], "host=");
927 user = getParameterValue(argv[1], "user=");
928 pass = getParameterValue(argv[1], "pass=");
929 socket = getParameterValue(argv[1], "socket=");
931 #if MYSQL_VERSION_ID >= 50000
932 /* enable automatic reconnection. */
933 if (mysql_options((MYSQL *) dbi->dbconn, MYSQL_OPT_RECONNECT,
934 &auto_reconnect) != 0) {
935 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
936 DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
937 "mysql driver failed to set "
938 "MYSQL_OPT_RECONNECT option, continuing");
940 #endif
942 for (j=0; dbc == NULL && j < 4; j++)
943 dbc = mysql_real_connect((MYSQL *) dbi->dbconn, host,
944 user, pass, dbname, port, socket,
945 flags);
947 /* let user know if we couldn't connect. */
948 if (dbc == NULL) {
949 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
950 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
951 "mysql driver failed to create "
952 "database connection after 4 attempts");
953 result = ISC_R_FAILURE;
954 goto full_cleanup;
957 /* return db connection via dbdata */
958 *dbdata = dbi;
960 result = ISC_R_SUCCESS;
961 goto cleanup;
963 full_cleanup:
965 destroy_sqldbinstance(dbi);
967 cleanup:
969 if (dbname != NULL)
970 isc_mem_free(ns_g_mctx, dbname);
971 if (host != NULL)
972 isc_mem_free(ns_g_mctx, host);
973 if (user != NULL)
974 isc_mem_free(ns_g_mctx, user);
975 if (pass != NULL)
976 isc_mem_free(ns_g_mctx, pass);
977 if (socket != NULL)
978 isc_mem_free(ns_g_mctx, socket);
981 return result;
985 * destroy the driver. Remember, only 1 copy of the driver's
986 * code is ever loaded, the driver has to remember which context it's
987 * operating in. This is done via use of the dbdata argument.
988 * so we really only need to clean it up since we are not using driverarg.
991 static void
992 mysql_destroy(void *driverarg, void *dbdata)
994 dbinstance_t *dbi;
996 UNUSED(driverarg);
998 dbi = (dbinstance_t *) dbdata;
1000 /* release DB connection */
1001 if (dbi->dbconn != NULL)
1002 mysql_close((MYSQL *) dbi->dbconn);
1004 /* destroy DB instance */
1005 destroy_sqldbinstance(dbi);
1008 /* pointers to all our runtime methods. */
1009 /* this is used during driver registration */
1010 /* i.e. in dlz_mysql_init below. */
1011 static dns_sdlzmethods_t dlz_mysql_methods = {
1012 mysql_create,
1013 mysql_destroy,
1014 mysql_findzone,
1015 mysql_lookup,
1016 mysql_authority,
1017 mysql_allnodes,
1018 mysql_allowzonexfr
1022 * Wrapper around dns_sdlzregister().
1024 isc_result_t
1025 dlz_mysql_init(void) {
1026 isc_result_t result;
1029 * Write debugging message to log
1031 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1032 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1033 "Registering DLZ mysql driver.");
1035 /* Driver is always threadsafe. Because of the way MySQL handles
1036 * threads the MySQL driver can only be used when bind is run single
1037 * threaded. Using MySQL with Bind running multi-threaded is not
1038 * allowed. When using the MySQL driver "-n1" should always be
1039 * passed to Bind to guarantee single threaded operation.
1041 result = dns_sdlzregister("mysql", &dlz_mysql_methods, NULL,
1042 DNS_SDLZFLAG_RELATIVEOWNER |
1043 DNS_SDLZFLAG_RELATIVERDATA |
1044 DNS_SDLZFLAG_THREADSAFE,
1045 ns_g_mctx, &dlz_mysql);
1046 /* if we can't register the driver, there are big problems. */
1047 if (result != ISC_R_SUCCESS) {
1048 UNEXPECTED_ERROR(__FILE__, __LINE__,
1049 "dns_sdlzregister() failed: %s",
1050 isc_result_totext(result));
1051 result = ISC_R_UNEXPECTED;
1055 return result;
1059 * Wrapper around dns_sdlzunregister().
1061 void
1062 dlz_mysql_clear(void) {
1065 * Write debugging message to log
1067 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1068 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1069 "Unregistering DLZ mysql driver.");
1071 /* unregister the driver. */
1072 if (dlz_mysql != NULL)
1073 dns_sdlzunregister(&dlz_mysql);
1076 #endif