Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / contrib / dlz / modules / mysql / dlz_mysql_dynamic.c
blob3d5e9cb3b8ff83854d6a83b6d705936b63629989
1 /* $NetBSD: dlz_mysql_dynamic.c,v 1.1.1.3 2014/12/10 03:34:31 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.
40 * Copyright (C) 2013 Internet Systems Consortium.
42 * Permission to use, copy, modify, and distribute this software for any
43 * purpose with or without fee is hereby granted, provided that the above
44 * copyright notice and this permission notice appear in all copies.
46 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
47 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
49 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
50 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
51 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
52 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
53 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
57 * This provides the externally loadable MySQL DLZ module, without
58 * update support
61 #include <stdio.h>
62 #include <string.h>
63 #include <stdarg.h>
64 #include <stdint.h>
65 #include <stdlib.h>
67 #include <dlz_minimal.h>
68 #include <dlz_list.h>
69 #include <dlz_dbi.h>
70 #include <dlz_pthread.h>
72 #include <mysql/mysql.h>
74 #define dbc_search_limit 30
75 #define ALLNODES 1
76 #define ALLOWXFR 2
77 #define AUTHORITY 3
78 #define FINDZONE 4
79 #define COUNTZONE 5
80 #define LOOKUP 6
82 #define safeGet(in) in == NULL ? "" : in
84 /*%
85 * Structure to hold everthing needed by this "instance" of the MySQL
86 * module remember, the module code is only loaded once, but may have
87 * many separate instances.
89 typedef struct {
90 #if PTHREADS
91 db_list_t *db; /*%< handle to a list of DB */
92 int dbcount;
93 #else
94 dbinstance_t *db; /*%< handle to DB */
95 #endif
97 unsigned int flags;
98 char *dbname;
99 char *host;
100 char *user;
101 char *pass;
102 char *socket;
103 int port;
105 /* Helper functions from the dlz_dlopen driver */
106 log_t *log;
107 dns_sdlz_putrr_t *putrr;
108 dns_sdlz_putnamedrr_t *putnamedrr;
109 dns_dlz_writeablezone_t *writeable_zone;
110 } mysql_instance_t;
112 /* forward references */
113 isc_result_t
114 dlz_findzonedb(void *dbdata, const char *name,
115 dns_clientinfomethods_t *methods,
116 dns_clientinfo_t *clientinfo);
118 void
119 dlz_destroy(void *dbdata);
121 static void
122 b9_add_helper(mysql_instance_t *db, const char *helper_name, void *ptr);
125 * Private methods
128 void
129 mysql_destroy(dbinstance_t *db) {
130 /* release DB connection */
131 if (db->dbconn != NULL)
132 mysql_close((MYSQL *) db->dbconn);
134 /* destroy DB instance */
135 destroy_dbinstance(db);
138 #if PTHREADS
140 * Properly cleans up a list of database instances.
141 * This function is only used when the module is compiled for
142 * multithreaded operation.
144 static void
145 mysql_destroy_dblist(db_list_t *dblist) {
146 dbinstance_t *ndbi = NULL;
147 dbinstance_t *dbi = NULL;
149 ndbi = DLZ_LIST_HEAD(*dblist);
150 while (ndbi != NULL) {
151 dbi = ndbi;
152 ndbi = DLZ_LIST_NEXT(dbi, link);
154 mysql_destroy(dbi);
157 /* release memory for the list structure */
158 free(dblist);
162 * Loops through the list of DB instances, attempting to lock
163 * on the mutex. If successful, the DBI is reserved for use
164 * and the thread can perform queries against the database.
165 * If the lock fails, the next one in the list is tried.
166 * looping continues until a lock is obtained, or until
167 * the list has been searched dbc_search_limit times.
168 * This function is only used when the module is compiled for
169 * multithreaded operation.
171 static dbinstance_t *
172 mysql_find_avail_conn(mysql_instance_t *mysql) {
173 dbinstance_t *dbi = NULL, *head;
174 int count = 0;
176 /* get top of list */
177 head = dbi = DLZ_LIST_HEAD(*(mysql->db));
179 /* loop through list */
180 while (count < dbc_search_limit) {
181 /* try to lock on the mutex */
182 if (dlz_mutex_trylock(&dbi->lock) == 0)
183 return (dbi); /* success, return the DBI for use. */
185 /* not successful, keep trying */
186 dbi = DLZ_LIST_NEXT(dbi, link);
188 /* check to see if we have gone to the top of the list. */
189 if (dbi == NULL) {
190 count++;
191 dbi = head;
195 mysql->log(ISC_LOG_INFO,
196 "MySQL module unable to find available connection "
197 "after searching %d times", count);
198 return (NULL);
200 #endif /* PTHREADS */
203 * Allocates memory for a new string, and then constructs the new
204 * string by "escaping" the input string. The new string is
205 * safe to be used in queries. This is necessary because we cannot
206 * be sure of what types of strings are passed to us, and we don't
207 * want special characters in the string causing problems.
209 static char *
210 mysqldrv_escape_string(MYSQL *mysql, const char *instr) {
212 char *outstr;
213 unsigned int len;
215 if (instr == NULL)
216 return (NULL);
218 len = strlen(instr);
219 outstr = malloc((2 * len * sizeof(char)) + 1);
220 if (outstr == NULL)
221 return (NULL);
223 mysql_real_escape_string(mysql, outstr, instr, len);
225 return (outstr);
229 * This function is the real core of the module. Zone, record
230 * and client strings are passed in (or NULL is passed if the
231 * string is not available). The type of query we want to run
232 * is indicated by the query flag, and the dbdata object is passed
233 * passed in to. dbdata really holds a single database instance.
234 * The function will construct and run the query, hopefully getting
235 * a result set.
237 static isc_result_t
238 mysql_get_resultset(const char *zone, const char *record,
239 const char *client, unsigned int query,
240 void *dbdata, MYSQL_RES **rs)
242 isc_result_t result;
243 dbinstance_t *dbi = NULL;
244 mysql_instance_t *db = (mysql_instance_t *)dbdata;
245 char *querystring = NULL;
246 unsigned int i = 0;
247 unsigned int j = 0;
248 int qres = 0;
250 #if PTHREADS
251 /* find an available DBI from the list */
252 dbi = mysql_find_avail_conn(db);
253 #else /* PTHREADS */
255 * only 1 DBI - no need to lock instance lock either
256 * only 1 thread in the whole process, no possible contention.
258 dbi = (dbinstance_t *)(db->db);
259 #endif /* PTHREADS */
261 if (dbi == NULL) {
262 result = ISC_R_FAILURE;
263 goto cleanup;
266 /* what type of query are we going to run? */
267 switch(query) {
268 case ALLNODES:
269 if (dbi->allnodes_q == NULL) {
270 result = ISC_R_NOTIMPLEMENTED;
271 goto cleanup;
273 break;
274 case ALLOWXFR:
275 if (dbi->allowxfr_q == NULL) {
276 result = ISC_R_NOTIMPLEMENTED;
277 goto cleanup;
279 break;
280 case AUTHORITY:
281 if (dbi->authority_q == NULL) {
282 result = ISC_R_NOTIMPLEMENTED;
283 goto cleanup;
285 break;
286 case FINDZONE:
287 if (dbi->findzone_q == NULL) {
288 db->log(ISC_LOG_DEBUG(2),
289 "No query specified for findzone. "
290 "Findzone requires a query");
291 result = ISC_R_FAILURE;
292 goto cleanup;
294 break;
295 case COUNTZONE:
296 if (dbi->countzone_q == NULL) {
297 result = ISC_R_NOTIMPLEMENTED;
298 goto cleanup;
300 break;
301 case LOOKUP:
302 if (dbi->lookup_q == NULL) {
303 db->log(ISC_LOG_DEBUG(2),
304 "No query specified for lookup. "
305 "Lookup requires a query");
306 result = ISC_R_FAILURE;
307 goto cleanup;
309 break;
310 default:
311 db->log(ISC_LOG_ERROR,
312 "Incorrect query flag passed to "
313 "mysql_get_resultset");
314 result = ISC_R_UNEXPECTED;
315 goto cleanup;
319 if (zone != NULL) {
320 if (dbi->zone != NULL)
321 free(dbi->zone);
323 dbi->zone = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
324 zone);
325 if (dbi->zone == NULL) {
326 result = ISC_R_NOMEMORY;
327 goto cleanup;
329 } else
330 dbi->zone = NULL;
332 if (record != NULL) {
333 if (dbi->record != NULL)
334 free(dbi->record);
336 dbi->record = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
337 record);
338 if (dbi->record == NULL) {
339 result = ISC_R_NOMEMORY;
340 goto cleanup;
342 } else
343 dbi->record = NULL;
345 if (client != NULL) {
346 if (dbi->client != NULL)
347 free(dbi->client);
349 dbi->client = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
350 client);
351 if (dbi->client == NULL) {
352 result = ISC_R_NOMEMORY;
353 goto cleanup;
355 } else
356 dbi->client = NULL;
359 * what type of query are we going to run? this time we build
360 * the actual query to run.
362 switch(query) {
363 case ALLNODES:
364 querystring = build_querystring(dbi->allnodes_q);
365 break;
366 case ALLOWXFR:
367 querystring = build_querystring(dbi->allowxfr_q);
368 break;
369 case AUTHORITY:
370 querystring = build_querystring(dbi->authority_q);
371 break;
372 case FINDZONE:
373 querystring = build_querystring(dbi->findzone_q);
374 break;
375 case COUNTZONE:
376 querystring = build_querystring(dbi->countzone_q);
377 break;
378 case LOOKUP:
379 querystring = build_querystring(dbi->lookup_q);
380 break;
381 default:
382 db->log(ISC_LOG_ERROR,
383 "Incorrect query flag passed to "
384 "mysql_get_resultset");
385 result = ISC_R_UNEXPECTED; goto cleanup;
388 if (querystring == NULL) {
389 result = ISC_R_NOMEMORY;
390 goto cleanup;
393 /* output the full query string when debugging */
394 db->log(ISC_LOG_DEBUG(1), "\nQuery String: %s\n", querystring);
396 /* attempt query up to 3 times. */
397 for (i = 0; i < 3; i++) {
398 qres = mysql_query((MYSQL *) dbi->dbconn, querystring);
399 if (qres == 0)
400 break;
401 for (j = 0; j < 4; j++)
402 if (mysql_ping((MYSQL *) dbi->dbconn) == 0)
403 break;
406 if (qres == 0) {
407 result = ISC_R_SUCCESS;
408 if (query != COUNTZONE) {
409 *rs = mysql_store_result((MYSQL *) dbi->dbconn);
410 if (*rs == NULL)
411 result = ISC_R_FAILURE;
413 } else
414 result = ISC_R_FAILURE;
416 cleanup:
417 if (dbi == NULL)
418 return (ISC_R_FAILURE);
420 if (dbi->zone != NULL) {
421 free(dbi->zone);
422 dbi->zone = NULL;
424 if (dbi->record != NULL) {
425 free(dbi->record);
426 dbi->record = NULL;
428 if (dbi->client != NULL) {
429 free(dbi->client);
430 dbi->client = NULL;
433 /* release the lock so another thread can use this dbi */
434 (void) dlz_mutex_unlock(&dbi->lock);
436 if (querystring != NULL)
437 free(querystring);
439 return (result);
443 * The processing of result sets for lookup and authority are
444 * exactly the same. So that functionality has been moved
445 * into this function to minimize code.
447 static isc_result_t
448 mysql_process_rs(mysql_instance_t *db, dns_sdlzlookup_t *lookup,
449 MYSQL_RES *rs)
451 isc_result_t result = ISC_R_NOTFOUND;
452 MYSQL_ROW row;
453 unsigned int fields;
454 unsigned int j;
455 char *tmpString;
456 char *endp;
457 int ttl;
459 fields = mysql_num_fields(rs); /* how many columns in result set */
460 row = mysql_fetch_row(rs); /* get a row from the result set */
461 while (row != NULL) {
462 unsigned int len = 0;
464 switch(fields) {
465 case 1:
467 * one column in rs, it's the data field. use
468 * default type of A record, and default TTL
469 * of 86400
471 result = db->putrr(lookup, "a", 86400, safeGet(row[0]));
472 break;
473 case 2:
475 * two columns, data field, and data type.
476 * use default TTL of 86400.
478 result = db->putrr(lookup, safeGet(row[0]), 86400,
479 safeGet(row[1]));
480 break;
481 case 3:
483 * three columns, all data no defaults.
484 * convert text to int, make sure it worked
485 * right.
487 ttl = strtol(safeGet(row[0]), &endp, 10);
488 if (*endp != '\0' || ttl < 0) {
489 db->log(ISC_LOG_ERROR,
490 "MySQL module ttl must be "
491 "a postive number");
492 return (ISC_R_FAILURE);
495 result = db->putrr(lookup, safeGet(row[1]), ttl,
496 safeGet(row[2]));
497 break;
498 default:
500 * more than 3 fields, concatenate the last
501 * ones together. figure out how long to make
502 * string.
504 for (j = 2; j < fields; j++)
505 len += strlen(safeGet(row[j])) + 1;
508 * allocate string memory, allow for NULL to
509 * term string
511 tmpString = malloc(len + 1);
512 if (tmpString == NULL) {
513 db->log(ISC_LOG_ERROR,
514 "MySQL module unable to allocate "
515 "memory for temporary string");
516 mysql_free_result(rs);
517 return (ISC_R_FAILURE);
520 strcpy(tmpString, safeGet(row[2]));
521 for (j = 3; j < fields; j++) {
522 strcat(tmpString, " ");
523 strcat(tmpString, safeGet(row[j]));
526 ttl = strtol(safeGet(row[0]), &endp, 10);
527 if (*endp != '\0' || ttl < 0) {
528 db->log(ISC_LOG_ERROR,
529 "MySQL module ttl must be "
530 "a postive number");
531 return (ISC_R_FAILURE);
534 result = db->putrr(lookup, safeGet(row[1]),
535 ttl, tmpString);
536 free(tmpString);
539 if (result != ISC_R_SUCCESS) {
540 mysql_free_result(rs);
541 db->log(ISC_LOG_ERROR,
542 "putrr returned error: %d", result);
543 return (ISC_R_FAILURE);
546 row = mysql_fetch_row(rs);
549 mysql_free_result(rs);
550 return (result);
554 * DLZ methods
557 /*% determine if the zone is supported by (in) the database */
558 isc_result_t
559 dlz_findzonedb(void *dbdata, const char *name,
560 dns_clientinfomethods_t *methods,
561 dns_clientinfo_t *clientinfo)
563 isc_result_t result;
564 MYSQL_RES *rs = NULL;
565 my_ulonglong rows;
566 mysql_instance_t *db = (mysql_instance_t *)dbdata;
568 UNUSED(methods);
569 UNUSED(clientinfo);
571 result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs);
572 if (result != ISC_R_SUCCESS || rs == NULL) {
573 if (rs != NULL)
574 mysql_free_result(rs);
576 db->log(ISC_LOG_ERROR,
577 "MySQL module unable to return "
578 "result set for findzone query");
580 return (ISC_R_FAILURE);
584 * if we returned any rows, the zone is supported.
586 rows = mysql_num_rows(rs);
587 mysql_free_result(rs);
588 if (rows > 0) {
589 mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL);
590 return (ISC_R_SUCCESS);
593 return (ISC_R_NOTFOUND);
596 /*% Determine if the client is allowed to perform a zone transfer */
597 isc_result_t
598 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
599 isc_result_t result;
600 mysql_instance_t *db = (mysql_instance_t *)dbdata;
601 MYSQL_RES *rs = NULL;
602 my_ulonglong rows;
604 /* first check if the zone is supported by the database. */
605 result = dlz_findzonedb(dbdata, name, NULL, NULL);
606 if (result != ISC_R_SUCCESS)
607 return (ISC_R_NOTFOUND);
610 * if we get to this point we know the zone is supported by
611 * the database the only questions now are is the zone
612 * transfer is allowed for this client and did the config file
613 * have an allow zone xfr query.
615 result = mysql_get_resultset(name, NULL, client, ALLOWXFR,
616 dbdata, &rs);
617 if (result == ISC_R_NOTIMPLEMENTED)
618 return (result);
620 if (result != ISC_R_SUCCESS || rs == NULL) {
621 if (rs != NULL)
622 mysql_free_result(rs);
623 db->log(ISC_LOG_ERROR,
624 "MySQL module unable to return "
625 "result set for allow xfr query");
626 return (ISC_R_FAILURE);
630 * count how many rows in result set; if we returned any,
631 * zone xfr is allowed.
633 rows = mysql_num_rows(rs);
634 mysql_free_result(rs);
635 if (rows > 0)
636 return (ISC_R_SUCCESS);
638 return (ISC_R_NOPERM);
642 * If the client is allowed to perform a zone transfer, the next order of
643 * business is to get all the nodes in the zone, so bind can respond to the
644 * query.
646 isc_result_t
647 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
648 isc_result_t result;
649 mysql_instance_t *db = (mysql_instance_t *)dbdata;
650 MYSQL_RES *rs = NULL;
651 MYSQL_ROW row;
652 unsigned int fields;
653 unsigned int j;
654 char *tmpString;
655 char *endp;
656 int ttl;
658 result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs);
659 if (result == ISC_R_NOTIMPLEMENTED)
660 return (result);
662 /* if we didn't get a result set, log an err msg. */
663 if (result != ISC_R_SUCCESS) {
664 db->log(ISC_LOG_ERROR,
665 "MySQL module unable to return "
666 "result set for all nodes query");
667 goto cleanup;
670 result = ISC_R_NOTFOUND;
672 fields = mysql_num_fields(rs); /* how many columns in result set */
673 row = mysql_fetch_row(rs); /* get a row from the result set */
674 while (row != NULL) {
675 if (fields < 4) {
676 db->log(ISC_LOG_ERROR,
677 "MySQL module too few fields returned "
678 "by all nodes query");
679 result = ISC_R_FAILURE;
680 goto cleanup;
683 ttl = strtol(safeGet(row[0]), &endp, 10);
684 if (*endp != '\0' || ttl < 0) {
685 db->log(ISC_LOG_ERROR,
686 "MySQL module ttl must be "
687 "a postive number");
688 result = ISC_R_FAILURE;
689 goto cleanup;
692 if (fields == 4) {
693 result = db->putnamedrr(allnodes, safeGet(row[2]),
694 safeGet(row[1]), ttl,
695 safeGet(row[3]));
696 } else {
697 unsigned int len = 0;
700 * more than 4 fields, concatenate the last
701 * ones together.
703 for (j = 3; j < fields; j++)
704 len += strlen(safeGet(row[j])) + 1;
706 tmpString = malloc(len + 1);
707 if (tmpString == NULL) {
708 db->log(ISC_LOG_ERROR,
709 "MySQL module unable to allocate "
710 "memory for temporary string");
711 result = ISC_R_FAILURE;
712 goto cleanup;
715 strcpy(tmpString, safeGet(row[3]));
716 for (j = 4; j < fields; j++) {
717 strcat(tmpString, " ");
718 strcat(tmpString, safeGet(row[j]));
721 result = db->putnamedrr(allnodes, safeGet(row[2]),
722 safeGet(row[1]),
723 ttl, tmpString);
724 free(tmpString);
727 if (result != ISC_R_SUCCESS) {
728 db->log(ISC_LOG_ERROR,
729 "putnamedrr returned error: %s", result);
730 result = ISC_R_FAILURE;
731 break;
734 row = mysql_fetch_row(rs);
737 cleanup:
738 if (rs != NULL)
739 mysql_free_result(rs);
741 return (result);
745 * If the lookup function does not return SOA or NS records for the zone,
746 * use this function to get that information for named.
748 isc_result_t
749 dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
750 isc_result_t result;
751 MYSQL_RES *rs = NULL;
752 mysql_instance_t *db = (mysql_instance_t *)dbdata;
754 result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs);
755 if (result == ISC_R_NOTIMPLEMENTED)
756 return (result);
758 if (result != ISC_R_SUCCESS) {
759 if (rs != NULL)
760 mysql_free_result(rs);
761 db->log(ISC_LOG_ERROR,
762 "MySQL module unable to return "
763 "result set for authority query");
764 return (ISC_R_FAILURE);
768 * lookup and authority result sets are processed in the same
769 * manner: mysql_process_rs does the job for both functions.
771 return (mysql_process_rs(db, lookup, rs));
774 /*% If zone is supported, lookup up a (or multiple) record(s) in it */
775 isc_result_t
776 dlz_lookup(const char *zone, const char *name,
777 void *dbdata, dns_sdlzlookup_t *lookup,
778 dns_clientinfomethods_t *methods,
779 dns_clientinfo_t *clientinfo)
781 isc_result_t result;
782 MYSQL_RES *rs = NULL;
783 mysql_instance_t *db = (mysql_instance_t *)dbdata;
785 UNUSED(methods);
786 UNUSED(clientinfo);
788 result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
790 /* if we didn't get a result set, log an err msg. */
791 if (result != ISC_R_SUCCESS) {
792 if (rs != NULL)
793 mysql_free_result(rs);
794 db->log(ISC_LOG_ERROR,
795 "MySQL module unable to return "
796 "result set for lookup query");
797 return (ISC_R_FAILURE);
801 * lookup and authority result sets are processed in the same
802 * manner: mysql_process_rs does the job for both functions.
804 return (mysql_process_rs(db, lookup, rs));
808 * Create an instance of the module.
810 isc_result_t
811 dlz_create(const char *dlzname, unsigned int argc, char *argv[],
812 void **dbdata, ...)
814 isc_result_t result = ISC_R_FAILURE;
815 mysql_instance_t *mysql = NULL;
816 dbinstance_t *dbi = NULL;
817 MYSQL *dbc;
818 char *tmp = NULL;
819 char *endp;
820 int j;
821 const char *helper_name;
822 #if MYSQL_VERSION_ID >= 50000
823 my_bool auto_reconnect = 1;
824 #endif
825 #if PTHREADS
826 int dbcount;
827 int i;
828 #endif /* PTHREADS */
829 va_list ap;
831 UNUSED(dlzname);
833 /* allocate memory for MySQL instance */
834 mysql = calloc(1, sizeof(mysql_instance_t));
835 if (mysql == NULL)
836 return (ISC_R_NOMEMORY);
837 memset(mysql, 0, sizeof(mysql_instance_t));
839 /* Fill in the helper functions */
840 va_start(ap, dbdata);
841 while ((helper_name = va_arg(ap, const char*)) != NULL)
842 b9_add_helper(mysql, helper_name, va_arg(ap, void*));
843 va_end(ap);
845 #if PTHREADS
846 /* if debugging, let user know we are multithreaded. */
847 mysql->log(ISC_LOG_DEBUG(1), "MySQL module running multithreaded");
848 #else /* PTHREADS */
849 /* if debugging, let user know we are single threaded. */
850 mysql->log(ISC_LOG_DEBUG(1), "MySQL module running single threaded");
851 #endif /* PTHREADS */
853 /* verify we have at least 4 arg's passed to the module */
854 if (argc < 4) {
855 mysql->log(ISC_LOG_ERROR,
856 "MySQL module requires "
857 "at least 4 command line args.");
858 return (ISC_R_FAILURE);
861 /* no more than 8 arg's should be passed to the module */
862 if (argc > 8) {
863 mysql->log(ISC_LOG_ERROR,
864 "MySQL module cannot accept "
865 "more than 7 command line args.");
866 return (ISC_R_FAILURE);
869 /* get db name - required */
870 mysql->dbname = get_parameter_value(argv[1], "dbname=");
871 if (mysql->dbname == NULL) {
872 mysql->log(ISC_LOG_ERROR,
873 "MySQL module requires a dbname parameter.");
874 result = ISC_R_FAILURE;
875 goto cleanup;
878 /* get db port. Not required, but must be > 0 if specified */
879 tmp = get_parameter_value(argv[1], "port=");
880 if (tmp == NULL)
881 mysql->port = 0;
882 else {
883 mysql->port = strtol(tmp, &endp, 10);
884 if (*endp != '\0' || mysql->port < 0) {
885 mysql->log(ISC_LOG_ERROR,
886 "Mysql module: port "
887 "must be a positive number.");
888 free(tmp);
889 result = ISC_R_FAILURE;
890 goto cleanup;
892 free(tmp);
895 mysql->host = get_parameter_value(argv[1], "host=");
896 mysql->user = get_parameter_value(argv[1], "user=");
897 mysql->pass = get_parameter_value(argv[1], "pass=");
898 mysql->socket = get_parameter_value(argv[1], "socket=");
900 mysql->flags = CLIENT_REMEMBER_OPTIONS;
902 tmp = get_parameter_value(argv[1], "compress=");
903 if (tmp != NULL) {
904 if (strcasecmp(tmp, "true") == 0)
905 mysql->flags |= CLIENT_COMPRESS;
906 free(tmp);
909 tmp = get_parameter_value(argv[1], "ssl=");
910 if (tmp != NULL) {
911 if (strcasecmp(tmp, "true") == 0)
912 mysql->flags |= CLIENT_SSL;
913 free(tmp);
916 tmp = get_parameter_value(argv[1], "space=");
917 if (tmp != NULL) {
918 if (strcasecmp(tmp, "ignore") == 0)
919 mysql->flags |= CLIENT_IGNORE_SPACE;
920 free(tmp);
923 #if PTHREADS
924 /* multithreaded build can have multiple DB connections */
925 tmp = get_parameter_value(argv[1], "threads=");
926 if (tmp == NULL)
927 dbcount = 1;
928 else {
929 dbcount = strtol(tmp, &endp, 10);
930 if (*endp != '\0' || dbcount < 1) {
931 mysql->log(ISC_LOG_ERROR,
932 "MySQL database connection count "
933 "must be positive.");
934 free(tmp);
935 result = ISC_R_FAILURE;
936 goto cleanup;
938 free(tmp);
941 /* allocate memory for database connection list */
942 mysql->db = calloc(1, sizeof(db_list_t));
943 if (mysql->db == NULL) {
944 result = ISC_R_NOMEMORY;
945 goto cleanup;
948 /* initialize DB connection list */
949 DLZ_LIST_INIT(*(mysql->db));
952 * create the appropriate number of database instances (DBI)
953 * append each new DBI to the end of the list
955 for (i = 0; i < dbcount; i++) {
956 #endif /* PTHREADS */
957 switch(argc) {
958 case 4:
959 result = build_dbinstance(NULL, NULL, NULL,
960 argv[2], argv[3], NULL,
961 &dbi, mysql->log);
962 break;
963 case 5:
964 result = build_dbinstance(NULL, NULL, argv[4],
965 argv[2], argv[3], NULL,
966 &dbi, mysql->log);
967 break;
968 case 6:
969 result = build_dbinstance(argv[5], NULL, argv[4],
970 argv[2], argv[3], NULL,
971 &dbi, mysql->log);
972 break;
973 case 7:
974 result = build_dbinstance(argv[5], argv[6], argv[4],
975 argv[2], argv[3], NULL,
976 &dbi, mysql->log);
977 break;
978 case 8:
979 result = build_dbinstance(argv[5], argv[6], argv[4],
980 argv[2], argv[3], argv[7],
981 &dbi, mysql->log);
982 break;
983 default:
984 result = ISC_R_FAILURE;
988 if (result != ISC_R_SUCCESS) {
989 mysql->log(ISC_LOG_ERROR,
990 "MySQL module could not create "
991 "database instance object.");
992 result = ISC_R_FAILURE;
993 goto cleanup;
996 #if PTHREADS
997 /* when multithreaded, build a list of DBI's */
998 DLZ_LINK_INIT(dbi, link);
999 DLZ_LIST_APPEND(*(mysql->db), dbi, link);
1000 #else
1002 * when single threaded, hold onto the one connection
1003 * instance.
1005 mysql->db = dbi;
1006 #endif
1008 /* create and set db connection */
1009 dbi->dbconn = mysql_init(NULL);
1010 if (dbi->dbconn == NULL) {
1011 mysql->log(ISC_LOG_ERROR,
1012 "MySQL module could not allocate "
1013 "memory for database connection");
1014 result = ISC_R_FAILURE;
1015 goto cleanup;
1018 dbc = NULL;
1020 #if MYSQL_VERSION_ID >= 50000
1021 /* enable automatic reconnection. */
1022 if (mysql_options((MYSQL *) dbi->dbconn, MYSQL_OPT_RECONNECT,
1023 &auto_reconnect) != 0) {
1024 mysql->log(ISC_LOG_WARNING,
1025 "MySQL module failed to set "
1026 "MYSQL_OPT_RECONNECT option, continuing");
1028 #endif
1030 for (j = 0; dbc == NULL && j < 4; j++) {
1031 dbc = mysql_real_connect((MYSQL *) dbi->dbconn,
1032 mysql->host, mysql->user,
1033 mysql->pass, mysql->dbname,
1034 mysql->port, mysql->socket,
1035 mysql->flags);
1036 if (dbc == NULL)
1037 mysql->log(ISC_LOG_ERROR,
1038 "MySQL connection failed: %s",
1039 mysql_error((MYSQL *) dbi->dbconn));
1042 if (dbc == NULL) {
1043 mysql->log(ISC_LOG_ERROR,
1044 "MySQL module failed to create "
1045 "database connection after 4 attempts");
1046 result = ISC_R_FAILURE;
1047 goto cleanup;
1050 #if PTHREADS
1051 /* set DBI = null for next loop through. */
1052 dbi = NULL;
1054 #endif /* PTHREADS */
1056 *dbdata = mysql;
1058 return (ISC_R_SUCCESS);
1060 cleanup:
1061 dlz_destroy(mysql);
1063 return (result);
1067 * Destroy the module.
1069 void
1070 dlz_destroy(void *dbdata) {
1071 mysql_instance_t *db = (mysql_instance_t *)dbdata;
1072 #if PTHREADS
1073 /* cleanup the list of DBI's */
1074 if (db->db != NULL)
1075 mysql_destroy_dblist((db_list_t *)(db->db));
1076 #else /* PTHREADS */
1077 mysql_destroy(db);
1078 #endif /* PTHREADS */
1080 if (db->dbname != NULL)
1081 free(db->dbname);
1082 if (db->host != NULL)
1083 free(db->host);
1084 if (db->user != NULL)
1085 free(db->user);
1086 if (db->pass != NULL)
1087 free(db->pass);
1088 if (db->socket != NULL)
1089 free(db->socket);
1093 * Return the version of the API
1096 dlz_version(unsigned int *flags) {
1097 *flags |= (DNS_SDLZFLAG_RELATIVEOWNER |
1098 DNS_SDLZFLAG_RELATIVERDATA |
1099 DNS_SDLZFLAG_THREADSAFE);
1100 return (DLZ_DLOPEN_VERSION);
1104 * Register a helper function from the bind9 dlz_dlopen driver
1106 static void
1107 b9_add_helper(mysql_instance_t *db, const char *helper_name, void *ptr) {
1108 if (strcmp(helper_name, "log") == 0)
1109 db->log = (log_t *)ptr;
1110 if (strcmp(helper_name, "putrr") == 0)
1111 db->putrr = (dns_sdlz_putrr_t *)ptr;
1112 if (strcmp(helper_name, "putnamedrr") == 0)
1113 db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
1114 if (strcmp(helper_name, "writeable_zone") == 0)
1115 db->writeable_zone = (dns_dlz_writeablezone_t *)ptr;