Annotation of src/external/mpl/bind/dist/contrib/dlz/drivers/dlz_mysql_driver.c, Revision 1.3
1.2 christos 1: /* $NetBSD: dlz_mysql_driver.c,v 1.1.1.9 2014/12/10 03:34:31 christos Exp $ */
1.1 christos 2:
3: /*
4: * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
5: *
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.
10: *
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.
19: *
20: * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21: * conceived and contributed by Rob Butler.
22: *
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.
27: *
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.
36: */
37:
38: /*
39: * Copyright (C) 1999-2001, 2016 Internet Systems Consortium, Inc. ("ISC")
40: *
41: * This Source Code Form is subject to the terms of the Mozilla Public
42: * License, v. 2.0. If a copy of the MPL was not distributed with this
43: * file, You can obtain one at http://mozilla.org/MPL/2.0/.
44: */
45:
46: #ifdef DLZ_MYSQL
1.3 ! christos 47: #include <mysql.h>
1.1 christos 48: #include <stdio.h>
1.3 ! christos 49: #include <stdlib.h>
1.1 christos 50: #include <string.h>
51:
52: #include <isc/mem.h>
53: #include <isc/platform.h>
54: #include <isc/print.h>
55: #include <isc/result.h>
56: #include <isc/string.h>
57: #include <isc/util.h>
58:
1.3 ! christos 59: #include <dns/log.h>
! 60: #include <dns/result.h>
! 61: #include <dns/sdlz.h>
1.1 christos 62:
1.3 ! christos 63: #include <dlz/dlz_mysql_driver.h>
1.1 christos 64: #include <dlz/sdlz_helper.h>
1.3 ! christos 65: #include <named/globals.h>
1.1 christos 66:
1.3 ! christos 67: #if !defined(LIBMARIADB) && MYSQL_VERSION_ID >= 80000
! 68: typedef bool my_bool;
! 69: #endif /* !defined(LIBMARIADB) && MYSQL_VERSION_ID >= 80000 */
1.1 christos 70:
71: static dns_sdlzimplementation_t *dlz_mysql = NULL;
72:
73: #define dbc_search_limit 30
1.3 ! christos 74: #define ALLNODES 1
! 75: #define ALLOWXFR 2
! 76: #define AUTHORITY 3
! 77: #define FINDZONE 4
! 78: #define COUNTZONE 5
! 79: #define LOOKUP 6
1.1 christos 80:
81: #define safeGet(in) in == NULL ? "" : in
82:
83: /*
84: * Private methods
85: */
86:
87: /*%
88: * Allocates memory for a new string, and then constructs the new
89: * string by "escaping" the input string. The new string is
90: * safe to be used in queries. This is necessary because we cannot
91: * be sure of what types of strings are passed to us, and we don't
92: * want special characters in the string causing problems.
93: */
94:
95: static char *
96: mysqldrv_escape_string(MYSQL *mysql, const char *instr) {
97: char *outstr;
98: unsigned int len;
99:
1.3 ! christos 100: if (instr == NULL) {
! 101: return (NULL);
! 102: }
1.1 christos 103:
104: len = strlen(instr);
105:
1.3 ! christos 106: outstr = isc_mem_allocate(named_g_mctx, (2 * len * sizeof(char)) + 1);
1.1 christos 107:
108: mysql_real_escape_string(mysql, outstr, instr, len);
109:
1.3 ! christos 110: return (outstr);
1.1 christos 111: }
112:
113: /*%
114: * This function is the real core of the driver. Zone, record
115: * and client strings are passed in (or NULL is passed if the
116: * string is not available). The type of query we want to run
117: * is indicated by the query flag, and the dbdata object is passed
118: * passed in to. dbdata really holds a single database instance.
119: * The function will construct and run the query, hopefully getting
120: * a result set.
121: */
122:
123: static isc_result_t
1.3 ! christos 124: mysql_get_resultset(const char *zone, const char *record, const char *client,
! 125: unsigned int query, void *dbdata, MYSQL_RES **rs) {
1.1 christos 126: isc_result_t result;
127: dbinstance_t *dbi = NULL;
128: char *querystring = NULL;
129: unsigned int i = 0;
130: unsigned int j = 0;
131: int qres = 0;
132:
1.3 ! christos 133: if (query != COUNTZONE) {
1.1 christos 134: REQUIRE(*rs == NULL);
1.3 ! christos 135: } else {
1.1 christos 136: REQUIRE(rs == NULL);
1.3 ! christos 137: }
1.1 christos 138:
139: /* get db instance / connection */
1.3 ! christos 140: dbi = (dbinstance_t *)dbdata;
1.1 christos 141:
142: /* if DBI is null, can't do anything else */
143: if (dbi == NULL) {
1.3 ! christos 144: return (ISC_R_FAILURE);
1.1 christos 145: }
146:
147: /* what type of query are we going to run? */
1.3 ! christos 148: switch (query) {
1.1 christos 149: case ALLNODES:
150: /*
151: * if the query was not passed in from the config file
152: * then we can't run it. return not_implemented, so
153: * it's like the code for that operation was never
154: * built into the driver.... AHHH flexibility!!!
155: */
156: if (dbi->allnodes_q == NULL) {
157: result = ISC_R_NOTIMPLEMENTED;
158: goto cleanup;
159: }
160: break;
161: case ALLOWXFR:
162: /* same as comments as ALLNODES */
163: if (dbi->allowxfr_q == NULL) {
164: result = ISC_R_NOTIMPLEMENTED;
165: goto cleanup;
166: }
167: break;
168: case AUTHORITY:
169: /* same as comments as ALLNODES */
170: if (dbi->authority_q == NULL) {
171: result = ISC_R_NOTIMPLEMENTED;
172: goto cleanup;
173: }
174: break;
175: case FINDZONE:
176: /* this is required. It's the whole point of DLZ! */
177: if (dbi->findzone_q == NULL) {
178: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
179: DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
180: "No query specified for findzone. "
181: "Findzone requires a query");
182: result = ISC_R_FAILURE;
183: goto cleanup;
184: }
185: break;
186: case COUNTZONE:
187: /* same as comments as ALLNODES */
188: if (dbi->countzone_q == NULL) {
189: result = ISC_R_NOTIMPLEMENTED;
190: goto cleanup;
191: }
192: break;
193: case LOOKUP:
194: /* this is required. It's also a major point of DLZ! */
195: if (dbi->lookup_q == NULL) {
196: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
197: DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
198: "No query specified for lookup. "
199: "Lookup requires a query");
200: result = ISC_R_FAILURE;
201: goto cleanup;
202: }
203: break;
204: default:
205: /*
206: * this should never happen. If it does, the code is
207: * screwed up!
208: */
209: UNEXPECTED_ERROR(__FILE__, __LINE__,
210: "Incorrect query flag passed to "
211: "mysql_get_resultset");
212: result = ISC_R_UNEXPECTED;
213: goto cleanup;
214: }
215:
216: /*
217: * was a zone string passed? If so, make it safe for use in
218: * queries.
219: */
220: if (zone != NULL) {
1.3 ! christos 221: dbi->zone = mysqldrv_escape_string((MYSQL *)dbi->dbconn, zone);
1.1 christos 222: if (dbi->zone == NULL) {
223: result = ISC_R_NOMEMORY;
224: goto cleanup;
225: }
1.3 ! christos 226: } else { /* no string passed, set the string pointer to NULL */
1.1 christos 227: dbi->zone = NULL;
228: }
229:
230: /*
231: * was a record string passed? If so, make it safe for use in
232: * queries.
233: */
234: if (record != NULL) {
1.3 ! christos 235: dbi->record = mysqldrv_escape_string((MYSQL *)dbi->dbconn,
1.1 christos 236: record);
237: if (dbi->record == NULL) {
238: result = ISC_R_NOMEMORY;
239: goto cleanup;
240: }
1.3 ! christos 241: } else { /* no string passed, set the string pointer to NULL */
1.1 christos 242: dbi->record = NULL;
243: }
244:
245: /*
246: * was a client string passed? If so, make it safe for use in
247: * queries.
248: */
249: if (client != NULL) {
1.3 ! christos 250: dbi->client = mysqldrv_escape_string((MYSQL *)dbi->dbconn,
1.1 christos 251: client);
252: if (dbi->client == NULL) {
253: result = ISC_R_NOMEMORY;
254: goto cleanup;
255: }
1.3 ! christos 256: } else { /* no string passed, set the string pointer to NULL */
1.1 christos 257: dbi->client = NULL;
258: }
259:
260: /*
261: * what type of query are we going to run? this time we build
262: * the actual query to run.
263: */
1.3 ! christos 264: switch (query) {
1.1 christos 265: case ALLNODES:
266: querystring = build_querystring(named_g_mctx, dbi->allnodes_q);
267: break;
268: case ALLOWXFR:
269: querystring = build_querystring(named_g_mctx, dbi->allowxfr_q);
270: break;
271: case AUTHORITY:
272: querystring = build_querystring(named_g_mctx, dbi->authority_q);
273: break;
274: case FINDZONE:
275: querystring = build_querystring(named_g_mctx, dbi->findzone_q);
276: break;
277: case COUNTZONE:
278: querystring = build_querystring(named_g_mctx, dbi->countzone_q);
279: break;
280: case LOOKUP:
281: querystring = build_querystring(named_g_mctx, dbi->lookup_q);
282: break;
283: default:
284: /*
285: * this should never happen. If it does, the code is
286: * screwed up!
287: */
288: UNEXPECTED_ERROR(__FILE__, __LINE__,
289: "Incorrect query flag passed to "
290: "mysql_get_resultset");
291: result = ISC_R_UNEXPECTED;
292: goto cleanup;
293: }
294:
295: /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
296: if (querystring == NULL) {
297: result = ISC_R_NOMEMORY;
298: goto cleanup;
299: }
300:
301: /*
302: * output the full query string during debug so we can see
303: * what lame error the query has.
304: */
1.3 ! christos 305: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ,
! 306: ISC_LOG_DEBUG(1), "\nQuery String: %s\n", querystring);
1.1 christos 307:
308: /* attempt query up to 3 times. */
1.3 ! christos 309: for (i = 0; i < 3; i++) {
! 310: qres = mysql_query((MYSQL *)dbi->dbconn, querystring);
! 311: if (qres == 0) {
1.1 christos 312: break;
1.3 ! christos 313: }
! 314: for (j = 0; mysql_ping((MYSQL *)dbi->dbconn) != 0 && j < 4; j++)
1.1 christos 315: ;
316: }
317:
318: if (qres == 0) {
319: result = ISC_R_SUCCESS;
320: if (query != COUNTZONE) {
1.3 ! christos 321: *rs = mysql_store_result((MYSQL *)dbi->dbconn);
! 322: if (*rs == NULL) {
1.1 christos 323: result = ISC_R_FAILURE;
1.3 ! christos 324: }
1.1 christos 325: }
326: } else {
327: result = ISC_R_FAILURE;
328: }
329:
1.3 ! christos 330: cleanup:
1.1 christos 331: /* it's always good to cleanup after yourself */
332:
333: /* free dbi->zone string */
1.3 ! christos 334: if (dbi->zone != NULL) {
1.1 christos 335: isc_mem_free(named_g_mctx, dbi->zone);
1.3 ! christos 336: }
1.1 christos 337:
338: /* free dbi->record string */
1.3 ! christos 339: if (dbi->record != NULL) {
1.1 christos 340: isc_mem_free(named_g_mctx, dbi->record);
1.3 ! christos 341: }
1.1 christos 342:
343: /* free dbi->client string */
1.3 ! christos 344: if (dbi->client != NULL) {
1.1 christos 345: isc_mem_free(named_g_mctx, dbi->client);
1.3 ! christos 346: }
1.1 christos 347:
348: /* release query string */
1.3 ! christos 349: if (querystring != NULL) {
1.1 christos 350: isc_mem_free(named_g_mctx, querystring);
1.3 ! christos 351: }
1.1 christos 352:
353: /* return result */
1.3 ! christos 354: return (result);
1.1 christos 355: }
356:
357: /*%
358: * The processing of result sets for lookup and authority are
359: * exactly the same. So that functionality has been moved
360: * into this function to minimize code.
361: */
362:
363: static isc_result_t
1.3 ! christos 364: mysql_process_rs(dns_sdlzlookup_t *lookup, MYSQL_RES *rs) {
1.1 christos 365: isc_result_t result = ISC_R_NOTFOUND;
366: MYSQL_ROW row;
367: unsigned int fields;
368: unsigned int j;
369: unsigned int len;
370: char *tmpString;
371: char *endp;
372: int ttl;
373:
1.3 ! christos 374: row = mysql_fetch_row(rs); /* get a row from the result set */
! 375: fields = mysql_num_fields(rs); /* how many columns in result set */
1.1 christos 376: while (row != NULL) {
1.3 ! christos 377: switch (fields) {
1.1 christos 378: case 1:
379: /*
380: * one column in rs, it's the data field. use
381: * default type of A record, and default TTL
382: * of 86400
383: */
384: result = dns_sdlz_putrr(lookup, "a", 86400,
385: safeGet(row[0]));
386: break;
387: case 2:
388: /*
389: * two columns, data field, and data type.
390: * use default TTL of 86400.
391: */
392: result = dns_sdlz_putrr(lookup, safeGet(row[0]), 86400,
393: safeGet(row[1]));
394: break;
395: case 3:
396: /*
397: * three columns, all data no defaults.
398: * convert text to int, make sure it worked
399: * right.
400: */
401: ttl = strtol(safeGet(row[0]), &endp, 10);
402: if (*endp != '\0' || ttl < 0) {
403: isc_log_write(dns_lctx,
404: DNS_LOGCATEGORY_DATABASE,
405: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
406: "mysql driver ttl must be "
1.3 ! christos 407: "a positive number");
1.1 christos 408: }
409: result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl,
410: safeGet(row[2]));
411: break;
412: default:
413: /*
414: * more than 3 fields, concatenate the last
415: * ones together. figure out how long to make
416: * string.
417: */
1.3 ! christos 418: for (j = 2, len = 0; j < fields; j++) {
1.1 christos 419: len += strlen(safeGet(row[j])) + 1;
420: }
421: /*
422: * allocate string memory, allow for NULL to
423: * term string
424: */
425: tmpString = isc_mem_allocate(named_g_mctx, len + 1);
426: /* copy field to tmpString */
427: strcpy(tmpString, safeGet(row[2]));
428:
429: /*
430: * concat the rest of fields together, space
431: * between each one.
432: */
1.3 ! christos 433: for (j = 3; j < fields; j++) {
1.1 christos 434: strcat(tmpString, " ");
435: strcat(tmpString, safeGet(row[j]));
436: }
437: /* convert text to int, make sure it worked right */
438: ttl = strtol(safeGet(row[0]), &endp, 10);
439: if (*endp != '\0' || ttl < 0) {
440: isc_log_write(dns_lctx,
441: DNS_LOGCATEGORY_DATABASE,
442: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
443: "mysql driver ttl must be "
1.3 ! christos 444: "a positive number");
1.1 christos 445: }
446: /* ok, now tell Bind about it. */
1.3 ! christos 447: result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl,
! 448: tmpString);
1.1 christos 449: /* done, get rid of this thing. */
450: isc_mem_free(named_g_mctx, tmpString);
451: }
452: /* I sure hope we were successful */
453: if (result != ISC_R_SUCCESS) {
454: /* nope, get rid of the Result set, and log a msg */
455: mysql_free_result(rs);
456: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
457: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
458: "dns_sdlz_putrr returned error. "
459: "Error code was: %s",
460: isc_result_totext(result));
461: return (ISC_R_FAILURE);
462: }
1.3 ! christos 463: row = mysql_fetch_row(rs); /* get next row */
1.1 christos 464: }
465:
466: /* free result set memory */
467: mysql_free_result(rs);
468:
469: /* return result code */
1.3 ! christos 470: return (result);
1.1 christos 471: }
472:
473: /*
474: * SDLZ interface methods
475: */
476:
477: /*% determine if the zone is supported by (in) the database */
478:
479: static isc_result_t
480: mysql_findzone(void *driverarg, void *dbdata, const char *name,
1.3 ! christos 481: dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo) {
1.1 christos 482: isc_result_t result;
483: MYSQL_RES *rs = NULL;
484: my_ulonglong rows;
485:
486: UNUSED(driverarg);
487: UNUSED(methods);
488: UNUSED(clientinfo);
489:
490: /* run the query and get the result set from the database. */
491: result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs);
492: /* if we didn't get a result set, log an err msg. */
493: if (result != ISC_R_SUCCESS || rs == NULL) {
1.3 ! christos 494: if (rs != NULL) {
1.1 christos 495: mysql_free_result(rs);
1.3 ! christos 496: }
1.1 christos 497: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
498: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
499: "mysql driver unable to return "
500: "result set for findzone query");
501: return (ISC_R_FAILURE);
502: }
503: /* count how many rows in result set */
504: rows = mysql_num_rows(rs);
505: /* get rid of result set, we are done with it. */
506: mysql_free_result(rs);
507:
508: /* if we returned any rows, zone is supported. */
509: if (rows > 0) {
510: mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL);
511: return (ISC_R_SUCCESS);
512: }
513:
514: /* no rows returned, zone is not supported. */
515: return (ISC_R_NOTFOUND);
516: }
517:
518: /*% Determine if the client is allowed to perform a zone transfer */
519: static isc_result_t
520: mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name,
1.3 ! christos 521: const char *client) {
1.1 christos 522: isc_result_t result;
523: MYSQL_RES *rs = NULL;
524: my_ulonglong rows;
525:
526: UNUSED(driverarg);
527:
528: /* first check if the zone is supported by the database. */
529: result = mysql_findzone(driverarg, dbdata, name, NULL, NULL);
1.3 ! christos 530: if (result != ISC_R_SUCCESS) {
1.1 christos 531: return (ISC_R_NOTFOUND);
1.3 ! christos 532: }
1.1 christos 533:
534: /*
535: * if we get to this point we know the zone is supported by
536: * the database the only questions now are is the zone
537: * transfer is allowed for this client and did the config file
538: * have an allow zone xfr query.
539: *
540: * Run our query, and get a result set from the database.
541: */
1.3 ! christos 542: result = mysql_get_resultset(name, NULL, client, ALLOWXFR, dbdata, &rs);
1.1 christos 543: /* if we get "not implemented", send it along. */
1.3 ! christos 544: if (result == ISC_R_NOTIMPLEMENTED) {
! 545: return (result);
! 546: }
1.1 christos 547: /* if we didn't get a result set, log an err msg. */
548: if (result != ISC_R_SUCCESS || rs == NULL) {
1.3 ! christos 549: if (rs != NULL) {
1.1 christos 550: mysql_free_result(rs);
1.3 ! christos 551: }
1.1 christos 552: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
553: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
554: "mysql driver unable to return "
555: "result set for allow xfr query");
556: return (ISC_R_FAILURE);
557: }
558: /* count how many rows in result set */
559: rows = mysql_num_rows(rs);
560: /* get rid of result set, we are done with it. */
561: mysql_free_result(rs);
562:
563: /* if we returned any rows, zone xfr is allowed. */
1.3 ! christos 564: if (rows > 0) {
1.1 christos 565: return (ISC_R_SUCCESS);
1.3 ! christos 566: }
1.1 christos 567:
568: /* no rows returned, zone xfr not allowed */
569: return (ISC_R_NOPERM);
570: }
571:
572: /*%
573: * If the client is allowed to perform a zone transfer, the next order of
574: * business is to get all the nodes in the zone, so bind can respond to the
575: * query.
576: */
577: static isc_result_t
578: mysql_allnodes(const char *zone, void *driverarg, void *dbdata,
1.3 ! christos 579: dns_sdlzallnodes_t *allnodes) {
1.1 christos 580: isc_result_t result;
581: MYSQL_RES *rs = NULL;
582: MYSQL_ROW row;
583: unsigned int fields;
584: unsigned int j;
585: unsigned int len;
586: char *tmpString;
587: char *endp;
588: int ttl;
589:
590: UNUSED(driverarg);
591:
592: /* run the query and get the result set from the database. */
593: result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs);
594: /* if we get "not implemented", send it along */
1.3 ! christos 595: if (result == ISC_R_NOTIMPLEMENTED) {
! 596: return (result);
! 597: }
1.1 christos 598: /* if we didn't get a result set, log an err msg. */
599: if (result != ISC_R_SUCCESS) {
1.3 ! christos 600: if (rs != NULL) {
1.1 christos 601: mysql_free_result(rs);
1.3 ! christos 602: }
1.1 christos 603: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
604: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
605: "mysql driver unable to return "
606: "result set for all nodes query");
607: return (ISC_R_FAILURE);
608: }
609:
610: result = ISC_R_NOTFOUND;
611:
1.3 ! christos 612: row = mysql_fetch_row(rs); /* get a row from the result set */
! 613: fields = mysql_num_fields(rs); /* how many columns in result set */
1.1 christos 614: while (row != NULL) {
1.3 ! christos 615: if (fields < 4) { /* gotta have at least 4 columns */
1.1 christos 616: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
617: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
618: "mysql driver too few fields returned "
619: "by all nodes query");
620: }
621: /* convert text to int, make sure it worked right */
622: ttl = strtol(safeGet(row[0]), &endp, 10);
623: if (*endp != '\0' || ttl < 0) {
624: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
625: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
626: "mysql driver ttl must be "
1.3 ! christos 627: "a positive number");
1.1 christos 628: }
629: if (fields == 4) {
630: /* tell Bind about it. */
631: result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
632: safeGet(row[1]), ttl,
633: safeGet(row[3]));
634: } else {
635: /*
636: * more than 4 fields, concatenate the last
637: * ones together. figure out how long to make
638: * string.
639: */
1.3 ! christos 640: for (j = 3, len = 0; j < fields; j++) {
1.1 christos 641: len += strlen(safeGet(row[j])) + 1;
642: }
643: /* allocate memory, allow for NULL to term string */
644: tmpString = isc_mem_allocate(named_g_mctx, len + 1);
645: /* copy this field to tmpString */
646: strcpy(tmpString, safeGet(row[3]));
1.3 ! christos 647: /* concatenate the rest, with spaces between */
! 648: for (j = 4; j < fields; j++) {
1.1 christos 649: strcat(tmpString, " ");
650: strcat(tmpString, safeGet(row[j]));
651: }
652: /* tell Bind about it. */
653: result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
1.3 ! christos 654: safeGet(row[1]), ttl,
! 655: tmpString);
1.1 christos 656: isc_mem_free(named_g_mctx, tmpString);
657: }
658: /* if we weren't successful, log err msg */
659: if (result != ISC_R_SUCCESS) {
660: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
661: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
662: "dns_sdlz_putnamedrr returned error. "
663: "Error code was: %s",
664: isc_result_totext(result));
665: result = ISC_R_FAILURE;
666: break;
667: }
668: /* get next row from the result set */
669: row = mysql_fetch_row(rs);
670: }
671:
672: /* free result set memory */
673: mysql_free_result(rs);
674:
1.3 ! christos 675: return (result);
1.1 christos 676: }
677:
678: /*% if the lookup function does not return SOA or NS records for the zone,
679: * use this function to get that information for Bind.
680: */
681:
682: static isc_result_t
683: mysql_authority(const char *zone, void *driverarg, void *dbdata,
1.3 ! christos 684: dns_sdlzlookup_t *lookup) {
1.1 christos 685: isc_result_t result;
686: MYSQL_RES *rs = NULL;
687:
688: UNUSED(driverarg);
689:
690: /* run the query and get the result set from the database. */
691: result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs);
692: /* if we get "not implemented", send it along */
1.3 ! christos 693: if (result == ISC_R_NOTIMPLEMENTED) {
! 694: return (result);
! 695: }
1.1 christos 696: /* if we didn't get a result set, log an err msg. */
697: if (result != ISC_R_SUCCESS) {
1.3 ! christos 698: if (rs != NULL) {
1.1 christos 699: mysql_free_result(rs);
1.3 ! christos 700: }
1.1 christos 701: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
702: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
703: "mysql driver unable to return "
704: "result set for authority query");
705: return (ISC_R_FAILURE);
706: }
707: /*
708: * lookup and authority result sets are processed in the same
709: * manner mysql_process_rs does the job for both functions.
710: */
1.3 ! christos 711: return (mysql_process_rs(lookup, rs));
1.1 christos 712: }
713:
714: /*% if zone is supported, lookup up a (or multiple) record(s) in it */
715: static isc_result_t
1.3 ! christos 716: mysql_lookup(const char *zone, const char *name, void *driverarg, void *dbdata,
! 717: dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods,
! 718: dns_clientinfo_t *clientinfo) {
1.1 christos 719: isc_result_t result;
720: MYSQL_RES *rs = NULL;
721:
722: UNUSED(driverarg);
723: UNUSED(methods);
724: UNUSED(clientinfo);
725:
726: /* run the query and get the result set from the database. */
727: result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
728: /* if we didn't get a result set, log an err msg. */
729: if (result != ISC_R_SUCCESS) {
1.3 ! christos 730: if (rs != NULL) {
1.1 christos 731: mysql_free_result(rs);
1.3 ! christos 732: }
1.1 christos 733: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
734: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
735: "mysql driver unable to return "
736: "result set for lookup query");
737: return (ISC_R_FAILURE);
738: }
739: /*
740: * lookup and authority result sets are processed in the same manner
741: * mysql_process_rs does the job for both functions.
742: */
1.3 ! christos 743: return (mysql_process_rs(lookup, rs));
1.1 christos 744: }
745:
746: /*%
747: * create an instance of the driver. Remember, only 1 copy of the driver's
748: * code is ever loaded, the driver has to remember which context it's
749: * operating in. This is done via use of the dbdata argument which is
750: * passed into all query functions.
751: */
752: static isc_result_t
753: mysql_create(const char *dlzname, unsigned int argc, char *argv[],
1.3 ! christos 754: void *driverarg, void **dbdata) {
1.1 christos 755: isc_result_t result;
756: dbinstance_t *dbi = NULL;
757: char *tmp = NULL;
758: char *dbname = NULL;
759: char *host = NULL;
760: char *user = NULL;
761: char *pass = NULL;
762: char *socket = NULL;
763: int port;
764: MYSQL *dbc;
765: char *endp;
766: int j;
767: unsigned int flags = 0;
768:
769: UNUSED(driverarg);
770: UNUSED(dlzname);
771:
772: /* verify we have at least 4 arg's passed to the driver */
773: if (argc < 4) {
774: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
775: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
776: "mysql driver requires "
777: "at least 4 command line args.");
778: return (ISC_R_FAILURE);
779: }
780:
781: /* no more than 8 arg's should be passed to the driver */
782: if (argc > 8) {
783: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
784: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
785: "mysql driver cannot accept "
786: "more than 7 command line args.");
787: return (ISC_R_FAILURE);
788: }
789:
1.3 ! christos 790: /* parse connection string and get parameters. */
1.1 christos 791:
792: /* get db name - required */
793: dbname = getParameterValue(argv[1], "dbname=");
794: if (dbname == NULL) {
795: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
796: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
797: "mysql driver requires a dbname parameter.");
798: result = ISC_R_FAILURE;
799: goto full_cleanup;
800: }
801:
802: /* get db port. Not required, but must be > 0 if specified */
803: tmp = getParameterValue(argv[1], "port=");
804: if (tmp == NULL) {
805: port = 0;
806: } else {
807: port = strtol(tmp, &endp, 10);
808: if (*endp != '\0' || port < 0) {
809: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
810: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
811: "Mysql driver port "
812: "must be a positive number.");
813: isc_mem_free(named_g_mctx, tmp);
814: result = ISC_R_FAILURE;
815: goto full_cleanup;
816: }
817: isc_mem_free(named_g_mctx, tmp);
818: }
819:
820: /* how many queries were passed in from config file? */
1.3 ! christos 821: switch (argc) {
1.1 christos 822: case 4:
823: result = build_sqldbinstance(named_g_mctx, NULL, NULL, NULL,
824: argv[2], argv[3], NULL, &dbi);
825: break;
826: case 5:
827: result = build_sqldbinstance(named_g_mctx, NULL, NULL, argv[4],
828: argv[2], argv[3], NULL, &dbi);
829: break;
830: case 6:
1.3 ! christos 831: result = build_sqldbinstance(named_g_mctx, argv[5], NULL,
! 832: argv[4], argv[2], argv[3], NULL,
! 833: &dbi);
1.1 christos 834: break;
835: case 7:
1.3 ! christos 836: result = build_sqldbinstance(named_g_mctx, argv[5], argv[6],
! 837: argv[4], argv[2], argv[3], NULL,
! 838: &dbi);
1.1 christos 839: break;
840: case 8:
1.3 ! christos 841: result = build_sqldbinstance(named_g_mctx, argv[5], argv[6],
! 842: argv[4], argv[2], argv[3], argv[7],
! 843: &dbi);
1.1 christos 844: break;
845: default:
846: /* not really needed, should shut up compiler. */
847: result = ISC_R_FAILURE;
848: }
849:
850: /* unsuccessful?, log err msg and cleanup. */
851: if (result != ISC_R_SUCCESS) {
852: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
853: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
854: "mysql driver could not create "
855: "database instance object.");
856: result = ISC_R_FAILURE;
857: goto cleanup;
858: }
859:
860: /* create and set db connection */
861: dbi->dbconn = mysql_init(NULL);
862:
863: /* if db connection cannot be created, log err msg and cleanup. */
864: if (dbi->dbconn == NULL) {
865: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
866: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
867: "mysql driver could not allocate "
868: "memory for database connection");
869: result = ISC_R_FAILURE;
870: goto full_cleanup;
871: }
872:
873: tmp = getParameterValue(argv[1], "compress=");
874: if (tmp != NULL) {
1.3 ! christos 875: if (strcasecmp(tmp, "true") == 0) {
1.1 christos 876: flags = CLIENT_COMPRESS;
1.3 ! christos 877: }
1.1 christos 878: isc_mem_free(named_g_mctx, tmp);
879: }
880:
881: tmp = getParameterValue(argv[1], "ssl=");
882: if (tmp != NULL) {
1.3 ! christos 883: if (strcasecmp(tmp, "true") == 0) {
1.1 christos 884: flags = flags | CLIENT_SSL;
1.3 ! christos 885: }
1.1 christos 886: isc_mem_free(named_g_mctx, tmp);
887: }
888:
889: tmp = getParameterValue(argv[1], "space=");
890: if (tmp != NULL) {
1.3 ! christos 891: if (strcasecmp(tmp, "ignore") == 0) {
1.1 christos 892: flags = flags | CLIENT_IGNORE_SPACE;
1.3 ! christos 893: }
1.1 christos 894: isc_mem_free(named_g_mctx, tmp);
895: }
896:
897: dbc = NULL;
898: host = getParameterValue(argv[1], "host=");
899: user = getParameterValue(argv[1], "user=");
900: pass = getParameterValue(argv[1], "pass=");
901: socket = getParameterValue(argv[1], "socket=");
902:
903: /* enable automatic reconnection. */
1.3 ! christos 904: if (mysql_options((MYSQL *)dbi->dbconn, MYSQL_OPT_RECONNECT,
! 905: &(my_bool){ 1 }) != 0)
! 906: {
1.1 christos 907: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
908: DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
909: "mysql driver failed to set "
1.3 ! christos 910: "MYSQL_OPT_RECONNECT option, "
! 911: "continuing");
1.1 christos 912: }
913:
1.3 ! christos 914: for (j = 0; dbc == NULL && j < 4; j++) {
! 915: dbc = mysql_real_connect((MYSQL *)dbi->dbconn, host, user, pass,
! 916: dbname, port, socket, flags);
! 917: }
1.1 christos 918:
919: /* let user know if we couldn't connect. */
920: if (dbc == NULL) {
921: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
922: DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
923: "mysql driver failed to create "
924: "database connection after 4 attempts");
925: result = ISC_R_FAILURE;
926: goto full_cleanup;
927: }
928:
929: /* return db connection via dbdata */
930: *dbdata = dbi;
931:
932: result = ISC_R_SUCCESS;
933: goto cleanup;
934:
1.3 ! christos 935: full_cleanup:
1.1 christos 936:
1.3 ! christos 937: if (dbi != NULL) {
1.1 christos 938: destroy_sqldbinstance(dbi);
1.3 ! christos 939: }
1.1 christos 940:
1.3 ! christos 941: cleanup:
1.1 christos 942:
1.3 ! christos 943: if (dbname != NULL) {
1.1 christos 944: isc_mem_free(named_g_mctx, dbname);
1.3 ! christos 945: }
! 946: if (host != NULL) {
1.1 christos 947: isc_mem_free(named_g_mctx, host);
1.3 ! christos 948: }
! 949: if (user != NULL) {
1.1 christos 950: isc_mem_free(named_g_mctx, user);
1.3 ! christos 951: }
! 952: if (pass != NULL) {
1.1 christos 953: isc_mem_free(named_g_mctx, pass);
1.3 ! christos 954: }
! 955: if (socket != NULL) {
1.1 christos 956: isc_mem_free(named_g_mctx, socket);
1.3 ! christos 957: }
1.1 christos 958:
1.3 ! christos 959: return (result);
1.1 christos 960: }
961:
962: /*%
963: * destroy the driver. Remember, only 1 copy of the driver's
964: * code is ever loaded, the driver has to remember which context it's
965: * operating in. This is done via use of the dbdata argument.
966: * so we really only need to clean it up since we are not using driverarg.
967: */
968:
969: static void
1.3 ! christos 970: mysql_destroy(void *driverarg, void *dbdata) {
1.1 christos 971: dbinstance_t *dbi;
972:
973: UNUSED(driverarg);
974:
1.3 ! christos 975: dbi = (dbinstance_t *)dbdata;
1.1 christos 976:
977: /* release DB connection */
1.3 ! christos 978: if (dbi->dbconn != NULL) {
! 979: mysql_close((MYSQL *)dbi->dbconn);
! 980: }
1.1 christos 981:
982: /* destroy DB instance */
983: destroy_sqldbinstance(dbi);
984: }
985:
986: /* pointers to all our runtime methods. */
987: /* this is used during driver registration */
988: /* i.e. in dlz_mysql_init below. */
989: static dns_sdlzmethods_t dlz_mysql_methods = {
990: mysql_create,
991: mysql_destroy,
992: mysql_findzone,
993: mysql_lookup,
994: mysql_authority,
995: mysql_allnodes,
996: mysql_allowzonexfr,
997: NULL,
998: NULL,
999: NULL,
1000: NULL,
1001: NULL,
1002: NULL,
1003: NULL,
1004: };
1005:
1006: /*%
1007: * Wrapper around dns_sdlzregister().
1008: */
1009: isc_result_t
1010: dlz_mysql_init(void) {
1011: isc_result_t result;
1012:
1013: /*
1014: * Write debugging message to log
1015: */
1.3 ! christos 1016: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ,
! 1017: ISC_LOG_DEBUG(2), "Registering DLZ mysql driver.");
1.1 christos 1018:
1019: /* Driver is always threadsafe. Because of the way MySQL handles
1.3 ! christos 1020: * threads the MySQL driver can only be used when bind is run single
! 1021: * threaded. Using MySQL with Bind running multi-threaded is not
! 1022: * allowed. When using the MySQL driver "-n1" should always be
! 1023: * passed to Bind to guarantee single threaded operation.
1.1 christos 1024: */
1025: result = dns_sdlzregister("mysql", &dlz_mysql_methods, NULL,
1026: DNS_SDLZFLAG_RELATIVEOWNER |
1.3 ! christos 1027: DNS_SDLZFLAG_RELATIVERDATA |
! 1028: DNS_SDLZFLAG_THREADSAFE,
1.1 christos 1029: named_g_mctx, &dlz_mysql);
1030: /* if we can't register the driver, there are big problems. */
1031: if (result != ISC_R_SUCCESS) {
1032: UNEXPECTED_ERROR(__FILE__, __LINE__,
1033: "dns_sdlzregister() failed: %s",
1034: isc_result_totext(result));
1035: result = ISC_R_UNEXPECTED;
1036: }
1037:
1.3 ! christos 1038: return (result);
1.1 christos 1039: }
1040:
1041: /*%
1042: * Wrapper around dns_sdlzunregister().
1043: */
1044: void
1045: dlz_mysql_clear(void) {
1046: /*
1047: * Write debugging message to log
1048: */
1.3 ! christos 1049: isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ,
! 1050: ISC_LOG_DEBUG(2), "Unregistering DLZ mysql driver.");
1.1 christos 1051:
1052: /* unregister the driver. */
1.3 ! christos 1053: if (dlz_mysql != NULL) {
1.1 christos 1054: dns_sdlzunregister(&dlz_mysql);
1.3 ! christos 1055: }
1.1 christos 1056: }
1057:
1.3 ! christos 1058: #endif /* ifdef DLZ_MYSQL */
CVSweb <webmaster@jp.NetBSD.org>