etc/services - sync with NetBSD-8
[minix.git] / external / bsd / bind / dist / contrib / dlz / drivers / dlz_mysql_driver.c
blob054566f496563047af08fc7b93138084710dc90b
1 /* $NetBSD: dlz_mysql_driver.c,v 1.6 2014/12/10 04:37:55 christos Exp $ */
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,
509 dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
511 isc_result_t result;
512 MYSQL_RES *rs = NULL;
513 my_ulonglong rows;
515 UNUSED(driverarg);
516 UNUSED(methods);
517 UNUSED(clientinfo);
519 /* run the query and get the result set from the database. */
520 result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs);
521 /* if we didn't get a result set, log an err msg. */
522 if (result != ISC_R_SUCCESS || rs == NULL) {
523 if (rs != NULL)
524 mysql_free_result(rs);
525 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
526 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
527 "mysql driver unable to return "
528 "result set for findzone query");
529 return (ISC_R_FAILURE);
531 /* count how many rows in result set */
532 rows = mysql_num_rows(rs);
533 /* get rid of result set, we are done with it. */
534 mysql_free_result(rs);
536 /* if we returned any rows, zone is supported. */
537 if (rows > 0) {
538 mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL);
539 return (ISC_R_SUCCESS);
542 /* no rows returned, zone is not supported. */
543 return (ISC_R_NOTFOUND);
546 /*% Determine if the client is allowed to perform a zone transfer */
547 static isc_result_t
548 mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name,
549 const char *client)
551 isc_result_t result;
552 MYSQL_RES *rs = NULL;
553 my_ulonglong rows;
555 UNUSED(driverarg);
557 /* first check if the zone is supported by the database. */
558 result = mysql_findzone(driverarg, dbdata, name, NULL, NULL);
559 if (result != ISC_R_SUCCESS)
560 return (ISC_R_NOTFOUND);
563 * if we get to this point we know the zone is supported by
564 * the database the only questions now are is the zone
565 * transfer is allowed for this client and did the config file
566 * have an allow zone xfr query.
568 * Run our query, and get a result set from the database.
570 result = mysql_get_resultset(name, NULL, client, ALLOWXFR,
571 dbdata, &rs);
572 /* if we get "not implemented", send it along. */
573 if (result == ISC_R_NOTIMPLEMENTED)
574 return result;
575 /* if we didn't get a result set, log an err msg. */
576 if (result != ISC_R_SUCCESS || rs == NULL) {
577 if (rs != NULL)
578 mysql_free_result(rs);
579 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
580 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
581 "mysql driver unable to return "
582 "result set for allow xfr query");
583 return (ISC_R_FAILURE);
585 /* count how many rows in result set */
586 rows = mysql_num_rows(rs);
587 /* get rid of result set, we are done with it. */
588 mysql_free_result(rs);
590 /* if we returned any rows, zone xfr is allowed. */
591 if (rows > 0)
592 return (ISC_R_SUCCESS);
594 /* no rows returned, zone xfr not allowed */
595 return (ISC_R_NOPERM);
599 * If the client is allowed to perform a zone transfer, the next order of
600 * business is to get all the nodes in the zone, so bind can respond to the
601 * query.
603 static isc_result_t
604 mysql_allnodes(const char *zone, void *driverarg, void *dbdata,
605 dns_sdlzallnodes_t *allnodes)
607 isc_result_t result;
608 MYSQL_RES *rs = NULL;
609 MYSQL_ROW row;
610 unsigned int fields;
611 unsigned int j;
612 unsigned int len;
613 char *tmpString;
614 char *endp;
615 int ttl;
617 UNUSED(driverarg);
619 /* run the query and get the result set from the database. */
620 result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs);
621 /* if we get "not implemented", send it along */
622 if (result == ISC_R_NOTIMPLEMENTED)
623 return result;
624 /* if we didn't get a result set, log an err msg. */
625 if (result != ISC_R_SUCCESS) {
626 if (rs != NULL)
627 mysql_free_result(rs);
628 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
629 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
630 "mysql driver unable to return "
631 "result set for all nodes query");
632 return (ISC_R_FAILURE);
635 result = ISC_R_NOTFOUND;
637 row = mysql_fetch_row(rs); /* get a row from the result set */
638 fields = mysql_num_fields(rs); /* how many columns in result set */
639 while (row != NULL) {
640 if (fields < 4) { /* gotta have at least 4 columns */
641 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
642 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
643 "mysql driver too few fields returned "
644 "by all nodes query");
646 /* convert text to int, make sure it worked right */
647 ttl = strtol(safeGet(row[0]), &endp, 10);
648 if (*endp != '\0' || ttl < 0) {
649 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
650 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
651 "mysql driver ttl must be "
652 "a postive number");
654 if (fields == 4) {
655 /* tell Bind about it. */
656 result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
657 safeGet(row[1]), ttl,
658 safeGet(row[3]));
659 } else {
661 * more than 4 fields, concatenate the last
662 * ones together. figure out how long to make
663 * string.
665 for (j=3, len=0; j < fields; j++) {
666 len += strlen(safeGet(row[j])) + 1;
668 /* allocate memory, allow for NULL to term string */
669 tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
670 if (tmpString == NULL) { /* we need more ram. */
671 isc_log_write(dns_lctx,
672 DNS_LOGCATEGORY_DATABASE,
673 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
674 "mysql driver unable "
675 "to allocate memory for "
676 "temporary string");
677 mysql_free_result(rs);
678 return (ISC_R_FAILURE);
680 /* copy this field to tmpString */
681 strcpy(tmpString, safeGet(row[3]));
682 /* concatonate the rest, with spaces between */
683 for (j=4; j < fields; j++) {
684 strcat(tmpString, " ");
685 strcat(tmpString, safeGet(row[j]));
687 /* tell Bind about it. */
688 result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
689 safeGet(row[1]),
690 ttl, tmpString);
691 isc_mem_free(ns_g_mctx, tmpString);
693 /* if we weren't successful, log err msg */
694 if (result != ISC_R_SUCCESS) {
695 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
696 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
697 "dns_sdlz_putnamedrr returned error. "
698 "Error code was: %s",
699 isc_result_totext(result));
700 result = ISC_R_FAILURE;
701 break;
703 /* get next row from the result set */
704 row = mysql_fetch_row(rs);
707 /* free result set memory */
708 mysql_free_result(rs);
710 return result;
713 /*% if the lookup function does not return SOA or NS records for the zone,
714 * use this function to get that information for Bind.
717 static isc_result_t
718 mysql_authority(const char *zone, void *driverarg, void *dbdata,
719 dns_sdlzlookup_t *lookup)
721 isc_result_t result;
722 MYSQL_RES *rs = NULL;
724 UNUSED(driverarg);
726 /* run the query and get the result set from the database. */
727 result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs);
728 /* if we get "not implemented", send it along */
729 if (result == ISC_R_NOTIMPLEMENTED)
730 return result;
731 /* if we didn't get a result set, log an err msg. */
732 if (result != ISC_R_SUCCESS) {
733 if (rs != NULL)
734 mysql_free_result(rs);
735 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
736 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
737 "mysql driver unable to return "
738 "result set for authority query");
739 return (ISC_R_FAILURE);
742 * lookup and authority result sets are processed in the same
743 * manner mysql_process_rs does the job for both functions.
745 return mysql_process_rs(lookup, rs);
748 /*% if zone is supported, lookup up a (or multiple) record(s) in it */
749 static isc_result_t
750 mysql_lookup(const char *zone, const char *name, void *driverarg,
751 void *dbdata, dns_sdlzlookup_t *lookup,
752 dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
754 isc_result_t result;
755 MYSQL_RES *rs = NULL;
757 UNUSED(driverarg);
758 UNUSED(methods);
759 UNUSED(clientinfo);
761 /* run the query and get the result set from the database. */
762 result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
763 /* if we didn't get a result set, log an err msg. */
764 if (result != ISC_R_SUCCESS) {
765 if (rs != NULL)
766 mysql_free_result(rs);
767 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
768 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
769 "mysql driver unable to return "
770 "result set for lookup query");
771 return (ISC_R_FAILURE);
774 * lookup and authority result sets are processed in the same manner
775 * mysql_process_rs does the job for both functions.
777 return mysql_process_rs(lookup, rs);
781 * create an instance of the driver. Remember, only 1 copy of the driver's
782 * code is ever loaded, the driver has to remember which context it's
783 * operating in. This is done via use of the dbdata argument which is
784 * passed into all query functions.
786 static isc_result_t
787 mysql_create(const char *dlzname, unsigned int argc, char *argv[],
788 void *driverarg, void **dbdata)
790 isc_result_t result;
791 dbinstance_t *dbi = NULL;
792 char *tmp = NULL;
793 char *dbname = NULL;
794 char *host = NULL;
795 char *user = NULL;
796 char *pass = NULL;
797 char *socket = NULL;
798 int port;
799 MYSQL *dbc;
800 char *endp;
801 int j;
802 unsigned int flags = 0;
803 #if MYSQL_VERSION_ID >= 50000
804 my_bool auto_reconnect = 1;
805 #endif
807 UNUSED(driverarg);
808 UNUSED(dlzname);
810 /* verify we have at least 4 arg's passed to the driver */
811 if (argc < 4) {
812 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
813 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
814 "mysql driver requires "
815 "at least 4 command line args.");
816 return (ISC_R_FAILURE);
819 /* no more than 8 arg's should be passed to the driver */
820 if (argc > 8) {
821 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
822 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
823 "mysql driver cannot accept "
824 "more than 7 command line args.");
825 return (ISC_R_FAILURE);
828 /* parse connection string and get paramters. */
830 /* get db name - required */
831 dbname = getParameterValue(argv[1], "dbname=");
832 if (dbname == NULL) {
833 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
834 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
835 "mysql driver requires a dbname parameter.");
836 result = ISC_R_FAILURE;
837 goto full_cleanup;
840 /* get db port. Not required, but must be > 0 if specified */
841 tmp = getParameterValue(argv[1], "port=");
842 if (tmp == NULL) {
843 port = 0;
844 } else {
845 port = strtol(tmp, &endp, 10);
846 if (*endp != '\0' || port < 0) {
847 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
848 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
849 "Mysql driver port "
850 "must be a positive number.");
851 isc_mem_free(ns_g_mctx, tmp);
852 result = ISC_R_FAILURE;
853 goto full_cleanup;
855 isc_mem_free(ns_g_mctx, tmp);
858 /* how many queries were passed in from config file? */
859 switch(argc) {
860 case 4:
861 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL,
862 argv[2], argv[3], NULL, &dbi);
863 break;
864 case 5:
865 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[4],
866 argv[2], argv[3], NULL, &dbi);
867 break;
868 case 6:
869 result = build_sqldbinstance(ns_g_mctx, argv[5], NULL, argv[4],
870 argv[2], argv[3], NULL, &dbi);
871 break;
872 case 7:
873 result = build_sqldbinstance(ns_g_mctx, argv[5],
874 argv[6], argv[4],
875 argv[2], argv[3], NULL, &dbi);
876 break;
877 case 8:
878 result = build_sqldbinstance(ns_g_mctx, argv[5],
879 argv[6], argv[4],
880 argv[2], argv[3], argv[7], &dbi);
881 break;
882 default:
883 /* not really needed, should shut up compiler. */
884 result = ISC_R_FAILURE;
887 /* unsuccessful?, log err msg and cleanup. */
888 if (result != ISC_R_SUCCESS) {
889 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
890 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
891 "mysql driver could not create "
892 "database instance object.");
893 result = ISC_R_FAILURE;
894 goto cleanup;
897 /* create and set db connection */
898 dbi->dbconn = mysql_init(NULL);
900 /* if db connection cannot be created, log err msg and cleanup. */
901 if (dbi->dbconn == NULL) {
902 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
903 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
904 "mysql driver could not allocate "
905 "memory for database connection");
906 result = ISC_R_FAILURE;
907 goto full_cleanup;
910 tmp = getParameterValue(argv[1], "compress=");
911 if (tmp != NULL) {
912 if (strcasecmp(tmp, "true") == 0)
913 flags = CLIENT_COMPRESS;
914 isc_mem_free(ns_g_mctx, tmp);
917 tmp = getParameterValue(argv[1], "ssl=");
918 if (tmp != NULL) {
919 if (strcasecmp(tmp, "true") == 0)
920 flags = flags | CLIENT_SSL;
921 isc_mem_free(ns_g_mctx, tmp);
924 tmp = getParameterValue(argv[1], "space=");
925 if (tmp != NULL) {
926 if (strcasecmp(tmp, "ignore") == 0)
927 flags = flags | CLIENT_IGNORE_SPACE;
928 isc_mem_free(ns_g_mctx, tmp);
931 dbc = NULL;
932 host = getParameterValue(argv[1], "host=");
933 user = getParameterValue(argv[1], "user=");
934 pass = getParameterValue(argv[1], "pass=");
935 socket = getParameterValue(argv[1], "socket=");
937 #if MYSQL_VERSION_ID >= 50000
938 /* enable automatic reconnection. */
939 if (mysql_options((MYSQL *) dbi->dbconn, MYSQL_OPT_RECONNECT,
940 &auto_reconnect) != 0) {
941 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
942 DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
943 "mysql driver failed to set "
944 "MYSQL_OPT_RECONNECT option, continuing");
946 #endif
948 for (j=0; dbc == NULL && j < 4; j++)
949 dbc = mysql_real_connect((MYSQL *) dbi->dbconn, host,
950 user, pass, dbname, port, socket,
951 flags);
953 /* let user know if we couldn't connect. */
954 if (dbc == NULL) {
955 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
956 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
957 "mysql driver failed to create "
958 "database connection after 4 attempts");
959 result = ISC_R_FAILURE;
960 goto full_cleanup;
963 /* return db connection via dbdata */
964 *dbdata = dbi;
966 result = ISC_R_SUCCESS;
967 goto cleanup;
969 full_cleanup:
971 if (dbi != NULL)
972 destroy_sqldbinstance(dbi);
974 cleanup:
976 if (dbname != NULL)
977 isc_mem_free(ns_g_mctx, dbname);
978 if (host != NULL)
979 isc_mem_free(ns_g_mctx, host);
980 if (user != NULL)
981 isc_mem_free(ns_g_mctx, user);
982 if (pass != NULL)
983 isc_mem_free(ns_g_mctx, pass);
984 if (socket != NULL)
985 isc_mem_free(ns_g_mctx, socket);
988 return result;
992 * destroy the driver. Remember, only 1 copy of the driver's
993 * code is ever loaded, the driver has to remember which context it's
994 * operating in. This is done via use of the dbdata argument.
995 * so we really only need to clean it up since we are not using driverarg.
998 static void
999 mysql_destroy(void *driverarg, void *dbdata)
1001 dbinstance_t *dbi;
1003 UNUSED(driverarg);
1005 dbi = (dbinstance_t *) dbdata;
1007 /* release DB connection */
1008 if (dbi->dbconn != NULL)
1009 mysql_close((MYSQL *) dbi->dbconn);
1011 /* destroy DB instance */
1012 destroy_sqldbinstance(dbi);
1015 /* pointers to all our runtime methods. */
1016 /* this is used during driver registration */
1017 /* i.e. in dlz_mysql_init below. */
1018 static dns_sdlzmethods_t dlz_mysql_methods = {
1019 mysql_create,
1020 mysql_destroy,
1021 mysql_findzone,
1022 mysql_lookup,
1023 mysql_authority,
1024 mysql_allnodes,
1025 mysql_allowzonexfr,
1026 NULL,
1027 NULL,
1028 NULL,
1029 NULL,
1030 NULL,
1031 NULL,
1032 NULL,
1036 * Wrapper around dns_sdlzregister().
1038 isc_result_t
1039 dlz_mysql_init(void) {
1040 isc_result_t result;
1043 * Write debugging message to log
1045 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1046 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1047 "Registering DLZ mysql driver.");
1049 /* Driver is always threadsafe. Because of the way MySQL handles
1050 * threads the MySQL driver can only be used when bind is run single
1051 * threaded. Using MySQL with Bind running multi-threaded is not
1052 * allowed. When using the MySQL driver "-n1" should always be
1053 * passed to Bind to guarantee single threaded operation.
1055 result = dns_sdlzregister("mysql", &dlz_mysql_methods, NULL,
1056 DNS_SDLZFLAG_RELATIVEOWNER |
1057 DNS_SDLZFLAG_RELATIVERDATA |
1058 DNS_SDLZFLAG_THREADSAFE,
1059 ns_g_mctx, &dlz_mysql);
1060 /* if we can't register the driver, there are big problems. */
1061 if (result != ISC_R_SUCCESS) {
1062 UNEXPECTED_ERROR(__FILE__, __LINE__,
1063 "dns_sdlzregister() failed: %s",
1064 isc_result_totext(result));
1065 result = ISC_R_UNEXPECTED;
1069 return result;
1073 * Wrapper around dns_sdlzunregister().
1075 void
1076 dlz_mysql_clear(void) {
1079 * Write debugging message to log
1081 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1082 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1083 "Unregistering DLZ mysql driver.");
1085 /* unregister the driver. */
1086 if (dlz_mysql != NULL)
1087 dns_sdlzunregister(&dlz_mysql);
1090 #endif