1 /* $NetBSD: dlz_mysql_driver.c,v 1.6 2014/12/10 04:37:55 christos Exp $ */
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
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
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.
64 #include <dns/result.h>
67 #include <isc/platform.h>
68 #include <isc/print.h>
69 #include <isc/result.h>
70 #include <isc/string.h>
73 #include <named/globals.h>
75 #include <dlz/sdlz_helper.h>
76 #include <dlz/dlz_mysql_driver.h>
80 static dns_sdlzimplementation_t
*dlz_mysql
= NULL
;
82 #define dbc_search_limit 30
90 #define safeGet(in) in == NULL ? "" : in
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.
105 mysqldrv_escape_string(MYSQL
*mysql
, const char *instr
) {
115 outstr
= isc_mem_allocate(ns_g_mctx
,(2 * len
* sizeof(char)) + 1);
119 mysql_real_escape_string(mysql
, outstr
, instr
, len
);
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
135 mysql_get_resultset(const char *zone
, const char *record
,
136 const char *client
, unsigned int query
,
137 void *dbdata
, MYSQL_RES
**rs
)
140 dbinstance_t
*dbi
= NULL
;
141 char *querystring
= NULL
;
146 if (query
!= COUNTZONE
)
147 REQUIRE(*rs
== NULL
);
151 /* get db instance / connection */
152 dbi
= (dbinstance_t
*) dbdata
;
154 /* if DBI is null, can't do anything else */
156 result
= ISC_R_FAILURE
;
160 /* what type of query are we going to run? */
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
;
175 /* same as comments as ALLNODES */
176 if (dbi
->allowxfr_q
== NULL
) {
177 result
= ISC_R_NOTIMPLEMENTED
;
182 /* same as comments as ALLNODES */
183 if (dbi
->authority_q
== NULL
) {
184 result
= ISC_R_NOTIMPLEMENTED
;
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
;
200 /* same as comments as ALLNODES */
201 if (dbi
->countzone_q
== NULL
) {
202 result
= ISC_R_NOTIMPLEMENTED
;
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
;
219 * this should never happen. If it does, the code is
222 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
223 "Incorrect query flag passed to "
224 "mysql_get_resultset");
225 result
= ISC_R_UNEXPECTED
;
231 * was a zone string passed? If so, make it safe for use in
235 dbi
->zone
= mysqldrv_escape_string((MYSQL
*) dbi
->dbconn
,
237 if (dbi
->zone
== NULL
) {
238 result
= ISC_R_NOMEMORY
;
241 } else { /* no string passed, set the string pointer to NULL */
246 * was a record string passed? If so, make it safe for use in
249 if (record
!= NULL
) {
250 dbi
->record
= mysqldrv_escape_string((MYSQL
*) dbi
->dbconn
,
252 if (dbi
->record
== NULL
) {
253 result
= ISC_R_NOMEMORY
;
256 } else { /* no string passed, set the string pointer to NULL */
261 * was a client string passed? If so, make it safe for use in
264 if (client
!= NULL
) {
265 dbi
->client
= mysqldrv_escape_string((MYSQL
*) dbi
->dbconn
,
267 if (dbi
->client
== NULL
) {
268 result
= ISC_R_NOMEMORY
;
271 } else { /* no string passed, set the string pointer to NULL */
276 * what type of query are we going to run? this time we build
277 * the actual query to run.
281 querystring
= build_querystring(ns_g_mctx
, dbi
->allnodes_q
);
284 querystring
= build_querystring(ns_g_mctx
, dbi
->allowxfr_q
);
287 querystring
= build_querystring(ns_g_mctx
, dbi
->authority_q
);
290 querystring
= build_querystring(ns_g_mctx
, dbi
->findzone_q
);
293 querystring
= build_querystring(ns_g_mctx
, dbi
->countzone_q
);
296 querystring
= build_querystring(ns_g_mctx
, dbi
->lookup_q
);
300 * this should never happen. If it does, the code is
303 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
304 "Incorrect query flag passed to "
305 "mysql_get_resultset");
306 result
= ISC_R_UNEXPECTED
;
310 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
311 if (querystring
== NULL
) {
312 result
= ISC_R_NOMEMORY
;
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
);
329 for (j
=0; mysql_ping((MYSQL
*) dbi
->dbconn
) != 0 && j
< 4; j
++)
334 result
= ISC_R_SUCCESS
;
335 if (query
!= COUNTZONE
) {
336 *rs
= mysql_store_result((MYSQL
*) dbi
->dbconn
);
338 result
= ISC_R_FAILURE
;
341 result
= ISC_R_FAILURE
;
346 /* it's always good to cleanup after yourself */
348 /* if we couldn't even get DBI, just return 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
);
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.
379 mysql_process_rs(dns_sdlzlookup_t
*lookup
, MYSQL_RES
*rs
)
381 isc_result_t result
= ISC_R_NOTFOUND
;
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
) {
396 * one column in rs, it's the data field. use
397 * default type of A record, and default TTL
400 result
= dns_sdlz_putrr(lookup
, "a", 86400,
405 * two columns, data field, and data type.
406 * use default TTL of 86400.
408 result
= dns_sdlz_putrr(lookup
, safeGet(row
[0]), 86400,
413 * three columns, all data no defaults.
414 * convert text to int, make sure it worked
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 "
425 result
= dns_sdlz_putrr(lookup
, safeGet(row
[1]), ttl
,
430 * more than 3 fields, concatenate the last
431 * ones together. figure out how long to make
434 for (j
=2, len
=0; j
< fields
; j
++) {
435 len
+= strlen(safeGet(row
[j
])) + 1;
438 * allocate string memory, allow for NULL to
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 "
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
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 "
474 /* ok, now tell Bind about it. */
475 result
= dns_sdlz_putrr(lookup
, safeGet(row
[1]),
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 */
502 * SDLZ interface methods
505 /*% determine if the zone is supported by (in) the database */
508 mysql_findzone(void *driverarg
, void *dbdata
, const char *name
,
509 dns_clientinfomethods_t
*methods
, dns_clientinfo_t
*clientinfo
)
512 MYSQL_RES
*rs
= NULL
;
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
) {
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. */
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 */
548 mysql_allowzonexfr(void *driverarg
, void *dbdata
, const char *name
,
552 MYSQL_RES
*rs
= NULL
;
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
,
572 /* if we get "not implemented", send it along. */
573 if (result
== ISC_R_NOTIMPLEMENTED
)
575 /* if we didn't get a result set, log an err msg. */
576 if (result
!= ISC_R_SUCCESS
|| 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. */
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
604 mysql_allnodes(const char *zone
, void *driverarg
, void *dbdata
,
605 dns_sdlzallnodes_t
*allnodes
)
608 MYSQL_RES
*rs
= NULL
;
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
)
624 /* if we didn't get a result set, log an err msg. */
625 if (result
!= ISC_R_SUCCESS
) {
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 "
655 /* tell Bind about it. */
656 result
= dns_sdlz_putnamedrr(allnodes
, safeGet(row
[2]),
657 safeGet(row
[1]), ttl
,
661 * more than 4 fields, concatenate the last
662 * ones together. figure out how long to make
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 "
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]),
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
;
703 /* get next row from the result set */
704 row
= mysql_fetch_row(rs
);
707 /* free result set memory */
708 mysql_free_result(rs
);
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.
718 mysql_authority(const char *zone
, void *driverarg
, void *dbdata
,
719 dns_sdlzlookup_t
*lookup
)
722 MYSQL_RES
*rs
= NULL
;
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
)
731 /* if we didn't get a result set, log an err msg. */
732 if (result
!= ISC_R_SUCCESS
) {
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 */
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
)
755 MYSQL_RES
*rs
= NULL
;
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
) {
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.
787 mysql_create(const char *dlzname
, unsigned int argc
, char *argv
[],
788 void *driverarg
, void **dbdata
)
791 dbinstance_t
*dbi
= NULL
;
802 unsigned int flags
= 0;
803 #if MYSQL_VERSION_ID >= 50000
804 my_bool auto_reconnect
= 1;
810 /* verify we have at least 4 arg's passed to the driver */
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 */
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
;
840 /* get db port. Not required, but must be > 0 if specified */
841 tmp
= getParameterValue(argv
[1], "port=");
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
,
850 "must be a positive number.");
851 isc_mem_free(ns_g_mctx
, tmp
);
852 result
= ISC_R_FAILURE
;
855 isc_mem_free(ns_g_mctx
, tmp
);
858 /* how many queries were passed in from config file? */
861 result
= build_sqldbinstance(ns_g_mctx
, NULL
, NULL
, NULL
,
862 argv
[2], argv
[3], NULL
, &dbi
);
865 result
= build_sqldbinstance(ns_g_mctx
, NULL
, NULL
, argv
[4],
866 argv
[2], argv
[3], NULL
, &dbi
);
869 result
= build_sqldbinstance(ns_g_mctx
, argv
[5], NULL
, argv
[4],
870 argv
[2], argv
[3], NULL
, &dbi
);
873 result
= build_sqldbinstance(ns_g_mctx
, argv
[5],
875 argv
[2], argv
[3], NULL
, &dbi
);
878 result
= build_sqldbinstance(ns_g_mctx
, argv
[5],
880 argv
[2], argv
[3], argv
[7], &dbi
);
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
;
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
;
910 tmp
= getParameterValue(argv
[1], "compress=");
912 if (strcasecmp(tmp
, "true") == 0)
913 flags
= CLIENT_COMPRESS
;
914 isc_mem_free(ns_g_mctx
, tmp
);
917 tmp
= getParameterValue(argv
[1], "ssl=");
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=");
926 if (strcasecmp(tmp
, "ignore") == 0)
927 flags
= flags
| CLIENT_IGNORE_SPACE
;
928 isc_mem_free(ns_g_mctx
, tmp
);
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");
948 for (j
=0; dbc
== NULL
&& j
< 4; j
++)
949 dbc
= mysql_real_connect((MYSQL
*) dbi
->dbconn
, host
,
950 user
, pass
, dbname
, port
, socket
,
953 /* let user know if we couldn't connect. */
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
;
963 /* return db connection via dbdata */
966 result
= ISC_R_SUCCESS
;
972 destroy_sqldbinstance(dbi
);
977 isc_mem_free(ns_g_mctx
, dbname
);
979 isc_mem_free(ns_g_mctx
, host
);
981 isc_mem_free(ns_g_mctx
, user
);
983 isc_mem_free(ns_g_mctx
, pass
);
985 isc_mem_free(ns_g_mctx
, socket
);
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.
999 mysql_destroy(void *driverarg
, void *dbdata
)
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
= {
1036 * Wrapper around dns_sdlzregister().
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
;
1073 * Wrapper around dns_sdlzunregister().
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
);