1 /* $NetBSD: dlz_postgres_driver.c,v 1.5 2014/12/10 04:37:55 christos Exp $ */
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 Internet Software Consortium.
40 *
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.
44 *
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.
53 */
54
55 #ifdef DLZ_POSTGRES
56
57 #include <config.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <stdlib.h>
61
62 #include <dns/log.h>
63 #include <dns/sdlz.h>
64 #include <dns/result.h>
65
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>
72
73 #include <named/globals.h>
74
75 #include <dlz/sdlz_helper.h>
76 #include <dlz/dlz_postgres_driver.h>
77
78 /* temporarily include time. */
79 #include <time.h>
80
81 #include <libpq-fe.h>
82
83 static dns_sdlzimplementation_t *dlz_postgres = NULL;
84
85 #define dbc_search_limit 30
86 #define ALLNODES 1
87 #define ALLOWXFR 2
88 #define AUTHORITY 3
89 #define FINDZONE 4
90 #define LOOKUP 5
91
92 /*
93 * Private methods
94 */
95
96 /* ---------------
97 * Escaping arbitrary strings to get valid SQL strings/identifiers.
98 *
99 * Replaces "\\" with "\\\\" and "'" with "''".
100 * length is the length of the buffer pointed to by
101 * from. The buffer at to must be at least 2*length + 1 characters
102 * long. A terminating NUL character is written.
103 *
104 * NOTICE!!!
105 * This function was borrowed directly from PostgreSQL's libpq.
106 * The function was originally called PQescapeString and renamed
107 * to postgres_makesafe to avoid a naming collision.
108 * PQescapeString is a new function made available in Postgres 7.2.
109 * For some reason the function is not properly exported on Win32
110 * builds making the function unavailable on Windows. Also, since
111 * this function is new it would require building this driver with
112 * the libpq 7.2. By borrowing this function the Windows problem
113 * is solved, and the dependence on libpq 7.2 is removed. Libpq is
114 * still required of course, but an older version should work now too.
115 *
116 * The copyright statements from the original file containing this
117 * function are included below:
118 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
119 * Portions Copyright (c) 1994, Regents of the University of California
120 * ---------------
121 */
122
123 static size_t
postgres_makesafe(char * to,const char * from,size_t length)124 postgres_makesafe(char *to, const char *from, size_t length)
125 {
126 const char *source = from;
127 char *target = to;
128 unsigned int remaining = length;
129
130 while (remaining > 0)
131 {
132 switch (*source)
133 {
134 case '\\':
135 *target = '\\';
136 target++;
137 *target = '\\';
138 /* target and remaining are updated below. */
139 break;
140
141 case '\'':
142 *target = '\'';
143 target++;
144 *target = '\'';
145 /* target and remaining are updated below. */
146 break;
147
148 default:
149 *target = *source;
150 /* target and remaining are updated below. */
151 }
152 source++;
153 target++;
154 remaining--;
155 }
156
157 /* Write the terminating NUL character. */
158 *target = '\0';
159
160 return target - to;
161 }
162
163 #ifdef ISC_PLATFORM_USETHREADS
164
165 /*%
166 * Properly cleans up a list of database instances.
167 * This function is only used when the driver is compiled for
168 * multithreaded operation.
169 */
170 static void
postgres_destroy_dblist(db_list_t * dblist)171 postgres_destroy_dblist(db_list_t *dblist)
172 {
173
174 dbinstance_t *ndbi = NULL;
175 dbinstance_t *dbi = NULL;
176
177 /* get the first DBI in the list */
178 ndbi = ISC_LIST_HEAD(*dblist);
179
180 /* loop through the list */
181 while (ndbi != NULL) {
182 dbi = ndbi;
183 /* get the next DBI in the list */
184 ndbi = ISC_LIST_NEXT(dbi, link);
185 /* release DB connection */
186 if (dbi->dbconn != NULL)
187 PQfinish((PGconn *) dbi->dbconn);
188 /* release all memory that comprised a DBI */
189 destroy_sqldbinstance(dbi);
190 }
191 /* release memory for the list structure */
192 isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
193 }
194
195 /*%
196 * Loops through the list of DB instances, attempting to lock
197 * on the mutex. If successful, the DBI is reserved for use
198 * and the thread can perform queries against the database.
199 * If the lock fails, the next one in the list is tried.
200 * looping continues until a lock is obtained, or until
201 * the list has been searched dbc_search_limit times.
202 * This function is only used when the driver is compiled for
203 * multithreaded operation.
204 */
205
206 static dbinstance_t *
postgres_find_avail_conn(db_list_t * dblist)207 postgres_find_avail_conn(db_list_t *dblist)
208 {
209 dbinstance_t *dbi = NULL;
210 dbinstance_t *head;
211 int count = 0;
212
213 /* get top of list */
214 head = dbi = ISC_LIST_HEAD(*dblist);
215
216 /* loop through list */
217 while (count < dbc_search_limit) {
218 /* try to lock on the mutex */
219 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
220 return dbi; /* success, return the DBI for use. */
221
222 /* not successful, keep trying */
223 dbi = ISC_LIST_NEXT(dbi, link);
224
225 /* check to see if we have gone to the top of the list. */
226 if (dbi == NULL) {
227 count++;
228 dbi = head;
229 }
230 }
231 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
232 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
233 "Postgres driver unable to find available connection "
234 "after searching %d times",
235 count);
236 return NULL;
237 }
238
239 #endif /* ISC_PLATFORM_USETHREADS */
240
241 /*%
242 * Allocates memory for a new string, and then constructs the new
243 * string by "escaping" the input string. The new string is
244 * safe to be used in queries. This is necessary because we cannot
245 * be sure of what types of strings are passed to us, and we don't
246 * want special characters in the string causing problems.
247 */
248
249 static char *
postgres_escape_string(const char * instr)250 postgres_escape_string(const char *instr) {
251
252 char *outstr;
253 unsigned int len;
254
255 if (instr == NULL)
256 return NULL;
257
258 len = strlen(instr);
259
260 outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
261 if (outstr == NULL)
262 return NULL;
263
264 postgres_makesafe(outstr, instr, len);
265 /* PQescapeString(outstr, instr, len); */
266
267 return outstr;
268 }
269
270 /*%
271 * This function is the real core of the driver. Zone, record
272 * and client strings are passed in (or NULL is passed if the
273 * string is not available). The type of query we want to run
274 * is indicated by the query flag, and the dbdata object is passed
275 * passed in to. dbdata really holds either:
276 * 1) a list of database instances (in multithreaded mode) OR
277 * 2) a single database instance (in single threaded mode)
278 * The function will construct the query and obtain an available
279 * database instance (DBI). It will then run the query and hopefully
280 * obtain a result set. Postgres is nice, in that once the result
281 * set is returned, we can make the db connection available for another
282 * thread to use, while this thread continues on. So, the DBI is made
283 * available ASAP by unlocking the instance_lock after we have cleaned
284 * it up properly.
285 */
286 static isc_result_t
postgres_get_resultset(const char * zone,const char * record,const char * client,unsigned int query,void * dbdata,PGresult ** rs)287 postgres_get_resultset(const char *zone, const char *record,
288 const char *client, unsigned int query,
289 void *dbdata, PGresult **rs)
290 {
291 isc_result_t result;
292 dbinstance_t *dbi = NULL;
293 char *querystring = NULL;
294 unsigned int i = 0;
295 unsigned int j = 0;
296
297 #if 0
298 /* temporarily get a unique thread # */
299 unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));
300 #endif
301
302 REQUIRE(*rs == NULL);
303
304 #if 0
305 /* temporary logging message */
306 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
307 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
308 "%d Getting DBI", dlz_thread_num);
309 #endif
310
311 /* get db instance / connection */
312 #ifdef ISC_PLATFORM_USETHREADS
313
314 /* find an available DBI from the list */
315 dbi = postgres_find_avail_conn((db_list_t *) dbdata);
316
317 #else /* ISC_PLATFORM_USETHREADS */
318
319 /*
320 * only 1 DBI - no need to lock instance lock either
321 * only 1 thread in the whole process, no possible contention.
322 */
323 dbi = (dbinstance_t *) dbdata;
324
325 #endif /* ISC_PLATFORM_USETHREADS */
326
327 #if 0
328 /* temporary logging message */
329 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
330 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
331 "%d Got DBI - checking query", dlz_thread_num);
332 #endif
333
334 /* if DBI is null, can't do anything else */
335 if (dbi == NULL) {
336 result = ISC_R_FAILURE;
337 goto cleanup;
338 }
339
340 /* what type of query are we going to run? */
341 switch(query) {
342 case ALLNODES:
343 /*
344 * if the query was not passed in from the config file
345 * then we can't run it. return not_implemented, so
346 * it's like the code for that operation was never
347 * built into the driver.... AHHH flexibility!!!
348 */
349 if (dbi->allnodes_q == NULL) {
350 result = ISC_R_NOTIMPLEMENTED;
351 goto cleanup;
352 }
353 break;
354 case ALLOWXFR:
355 /* same as comments as ALLNODES */
356 if (dbi->allowxfr_q == NULL) {
357 result = ISC_R_NOTIMPLEMENTED;
358 goto cleanup;
359 }
360 break;
361 case AUTHORITY:
362 /* same as comments as ALLNODES */
363 if (dbi->authority_q == NULL) {
364 result = ISC_R_NOTIMPLEMENTED;
365 goto cleanup;
366 }
367 break;
368 case FINDZONE:
369 /* this is required. It's the whole point of DLZ! */
370 if (dbi->findzone_q == NULL) {
371 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
372 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
373 "No query specified for findzone. "
374 "Findzone requires a query");
375 result = ISC_R_FAILURE;
376 goto cleanup;
377 }
378 break;
379 case LOOKUP:
380 /* this is required. It's also a major point of DLZ! */
381 if (dbi->lookup_q == NULL) {
382 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
383 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
384 "No query specified for lookup. "
385 "Lookup requires a query");
386 result = ISC_R_FAILURE;
387 goto cleanup;
388 }
389 break;
390 default:
391 /*
392 * this should never happen. If it does, the code is
393 * screwed up!
394 */
395 UNEXPECTED_ERROR(__FILE__, __LINE__,
396 "Incorrect query flag passed to "
397 "postgres_get_resultset");
398 result = ISC_R_UNEXPECTED;
399 goto cleanup;
400 }
401
402 #if 0
403 /* temporary logging message */
404 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
405 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
406 "%d checked query", dlz_thread_num);
407 #endif
408
409 /*
410 * was a zone string passed? If so, make it safe for use in
411 * queries.
412 */
413 if (zone != NULL) {
414 dbi->zone = postgres_escape_string(zone);
415 if (dbi->zone == NULL) {
416 result = ISC_R_NOMEMORY;
417 goto cleanup;
418 }
419 } else { /* no string passed, set the string pointer to NULL */
420 dbi->zone = NULL;
421 }
422
423 #if 0
424 /* temporary logging message */
425 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
426 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
427 "%d did zone", dlz_thread_num);
428 #endif
429
430 /*
431 * was a record string passed? If so, make it safe for use in
432 * queries.
433 */
434 if (record != NULL) {
435 dbi->record = postgres_escape_string(record);
436 if (dbi->record == NULL) {
437 result = ISC_R_NOMEMORY;
438 goto cleanup;
439 }
440 } else { /* no string passed, set the string pointer to NULL */
441 dbi->record = NULL;
442 }
443
444
445 #if 0
446 /* temporary logging message */
447 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
448 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
449 "%d did record", dlz_thread_num);
450 #endif
451
452 /*
453 * was a client string passed? If so, make it safe for use in
454 * queries.
455 */
456 if (client != NULL) {
457 dbi->client = postgres_escape_string(client);
458 if (dbi->client == NULL) {
459 result = ISC_R_NOMEMORY;
460 goto cleanup;
461 }
462 } else { /* no string passed, set the string pointer to NULL */
463 dbi->client = NULL;
464 }
465
466 #if 0
467 /* temporary logging message */
468 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
469 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
470 "%d did client", dlz_thread_num);
471 #endif
472
473 /*
474 * what type of query are we going to run?
475 * this time we build the actual query to run.
476 */
477 switch(query) {
478 case ALLNODES:
479 querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
480 break;
481 case ALLOWXFR:
482 querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
483 break;
484 case AUTHORITY:
485 querystring = build_querystring(ns_g_mctx, dbi->authority_q);
486 break;
487 case FINDZONE:
488 querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
489 break;
490 case LOOKUP:
491 querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
492 break;
493 default:
494 /*
495 * this should never happen. If it does, the code is
496 * screwed up!
497 */
498 UNEXPECTED_ERROR(__FILE__, __LINE__,
499 "Incorrect query flag passed to "
500 "postgres_get_resultset");
501 result = ISC_R_UNEXPECTED;
502 goto cleanup;
503 }
504
505 #if 0
506 /* temporary logging message */
507 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
508 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
509 "%d built query", dlz_thread_num);
510 #endif
511
512 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
513 if (querystring == NULL) {
514 result = ISC_R_NOMEMORY;
515 goto cleanup;
516 }
517
518 #if 0
519 /* temporary logging message */
520 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
521 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
522 "%d query is '%s'", dlz_thread_num, querystring);
523 #endif
524
525 /*
526 * output the full query string during debug so we can see
527 * what lame error the query has.
528 */
529 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
530 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
531 "\nQuery String: %s\n", querystring);
532
533 /* attempt query up to 3 times. */
534 for (j=0; j < 3; j++) {
535 #if 0
536 /* temporary logging message */
537 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
538 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
539 "%d executing query for %d time",
540 dlz_thread_num, j);
541 #endif
542 /* try to get result set */
543 *rs = PQexec((PGconn *)dbi->dbconn, querystring );
544 result = ISC_R_SUCCESS;
545 /*
546 * if result set is null, reset DB connection, max 3
547 * attempts.
548 */
549 for (i=0; *rs == NULL && i < 3; i++) {
550 #if 0
551 /* temporary logging message */
552 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
553 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
554 "%d resetting connection",
555 dlz_thread_num);
556 #endif
557 result = ISC_R_FAILURE;
558 PQreset((PGconn *) dbi->dbconn);
559 /* connection ok, break inner loop */
560 if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK)
561 break;
562 }
563 /* result set ok, break outter loop */
564 if (PQresultStatus(*rs) == PGRES_TUPLES_OK) {
565 #if 0
566 /* temporary logging message */
567 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
568 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
569 "%d rs ok", dlz_thread_num);
570 #endif
571 break;
572 } else {
573 /* we got a result set object, but it's not right. */
574 #if 0
575 /* temporary logging message */
576 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
577 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
578 "%d clearing rs", dlz_thread_num);
579 #endif
580 PQclear(*rs); /* get rid of it */
581 /* in case this was the last attempt */
582 *rs = NULL;
583 result = ISC_R_FAILURE;
584 }
585 }
586
587 cleanup:
588 /* it's always good to cleanup after yourself */
589
590 #if 0
591 /* temporary logging message */
592 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
593 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
594 "%d cleaning up", dlz_thread_num);
595 #endif
596
597 /* if we couldn't even allocate DBI, just return NULL */
598 if (dbi == NULL)
599 return ISC_R_FAILURE;
600
601 /* free dbi->zone string */
602 if (dbi->zone != NULL)
603 isc_mem_free(ns_g_mctx, dbi->zone);
604
605 /* free dbi->record string */
606 if (dbi->record != NULL)
607 isc_mem_free(ns_g_mctx, dbi->record);
608
609 /* free dbi->client string */
610 if (dbi->client != NULL)
611 isc_mem_free(ns_g_mctx, dbi->client);
612
613 #ifdef ISC_PLATFORM_USETHREADS
614
615 #if 0
616 /* temporary logging message */
617 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
618 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
619 "%d unlocking mutex", dlz_thread_num);
620 #endif
621
622 /* release the lock so another thread can use this dbi */
623 isc_mutex_unlock(&dbi->instance_lock);
624
625 #endif /* ISC_PLATFORM_USETHREADS */
626
627 /* release query string */
628 if (querystring != NULL)
629 isc_mem_free(ns_g_mctx, querystring );
630
631 #if 0
632 /* temporary logging message */
633 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
634 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
635 "%d returning", dlz_thread_num);
636 #endif
637
638 /* return result */
639 return result;
640 }
641
642 /*%
643 * The processing of result sets for lookup and authority are
644 * exactly the same. So that functionality has been moved
645 * into this function to minimize code.
646 */
647
648 static isc_result_t
postgres_process_rs(dns_sdlzlookup_t * lookup,PGresult * rs)649 postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs)
650 {
651 isc_result_t result;
652 unsigned int i;
653 unsigned int rows;
654 unsigned int fields;
655 unsigned int j;
656 unsigned int len;
657 char *tmpString;
658 char *endp;
659 int ttl;
660
661 rows = PQntuples(rs); /* how many rows in result set */
662 fields = PQnfields(rs); /* how many columns in result set */
663 for (i=0; i < rows; i++) {
664 switch(fields) {
665 case 1:
666 /*
667 * one column in rs, it's the data field. use
668 * default type of A record, and default TTL
669 * of 86400
670 */
671 result = dns_sdlz_putrr(lookup, "a", 86400,
672 PQgetvalue(rs, i, 0));
673 break;
674 case 2:
675 /* two columns, data field, and data type.
676 * use default TTL of 86400.
677 */
678 result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0),
679 86400, PQgetvalue(rs, i, 1));
680 break;
681 case 3:
682 /* three columns, all data no defaults.
683 * convert text to int, make sure it worked
684 * right.
685 */
686 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
687 if (*endp != '\0' || ttl < 0) {
688 isc_log_write(dns_lctx,
689 DNS_LOGCATEGORY_DATABASE,
690 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
691 "Postgres driver ttl must be "
692 "a positive number");
693 }
694 result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
695 ttl, PQgetvalue(rs, i, 2));
696 break;
697 default:
698 /*
699 * more than 3 fields, concatenate the last
700 * ones together. figure out how long to make
701 * string
702 */
703 for (j=2, len=0; j < fields; j++) {
704 len += strlen(PQgetvalue(rs, i, j)) + 1;
705 }
706 /*
707 * allocate string memory, allow for NULL to
708 * term string
709 */
710 tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
711 if (tmpString == NULL) {
712 /* major bummer, need more ram */
713 isc_log_write(dns_lctx,
714 DNS_LOGCATEGORY_DATABASE,
715 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
716 "Postgres driver unable to "
717 "allocate memory for "
718 "temporary string");
719 PQclear(rs);
720 return (ISC_R_FAILURE); /* Yeah, I'd say! */
721 }
722 /* copy field to tmpString */
723 strcpy(tmpString, PQgetvalue(rs, i, 2));
724 /*
725 * concat the rest of fields together, space
726 * between each one.
727 */
728 for (j=3; j < fields; j++) {
729 strcat(tmpString, " ");
730 strcat(tmpString, PQgetvalue(rs, i, j));
731 }
732 /* convert text to int, make sure it worked right */
733 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
734 if (*endp != '\0' || ttl < 0) {
735 isc_log_write(dns_lctx,
736 DNS_LOGCATEGORY_DATABASE,
737 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
738 "Postgres driver ttl must be "
739 "a postive number");
740 }
741 /* ok, now tell Bind about it. */
742 result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
743 ttl, tmpString);
744 /* done, get rid of this thing. */
745 isc_mem_free(ns_g_mctx, tmpString);
746 }
747 /* I sure hope we were successful */
748 if (result != ISC_R_SUCCESS) {
749 /* nope, get rid of the Result set, and log a msg */
750 PQclear(rs);
751 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
752 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
753 "dns_sdlz_putrr returned error. "
754 "Error code was: %s",
755 isc_result_totext(result));
756 return (ISC_R_FAILURE);
757 }
758 }
759
760 /* free result set memory */
761 PQclear(rs);
762
763 /* if we did return results, we are successful */
764 if (rows > 0)
765 return (ISC_R_SUCCESS);
766
767 /* empty result set, no data found */
768 return (ISC_R_NOTFOUND);
769 }
770
771 /*
772 * SDLZ interface methods
773 */
774
775 /*% determine if the zone is supported by (in) the database */
776
777 static isc_result_t
postgres_findzone(void * driverarg,void * dbdata,const char * name,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)778 postgres_findzone(void *driverarg, void *dbdata, const char *name,
779 dns_clientinfomethods_t *methods,
780 dns_clientinfo_t *clientinfo)
781 {
782 isc_result_t result;
783 PGresult *rs = NULL;
784 unsigned int rows;
785
786 UNUSED(driverarg);
787 UNUSED(methods);
788 UNUSED(clientinfo);
789
790 /* run the query and get the result set from the database. */
791 result = postgres_get_resultset(name, NULL, NULL,
792 FINDZONE, dbdata, &rs);
793 /* if we didn't get a result set, log an err msg. */
794 if (result != ISC_R_SUCCESS) {
795 if (rs != NULL)
796 PQclear(rs);
797 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
798 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
799 "Postgres driver unable to return "
800 "result set for findzone query");
801 return (ISC_R_FAILURE);
802 }
803 /* count how many rows in result set */
804 rows = PQntuples(rs);
805 /* get rid of result set, we are done with it. */
806 PQclear(rs);
807
808 /* if we returned any rows, zone is supported. */
809 if (rows > 0)
810 return (ISC_R_SUCCESS);
811
812 /* no rows returned, zone is not supported. */
813 return (ISC_R_NOTFOUND);
814 }
815
816 /*% Determine if the client is allowed to perform a zone transfer */
817 static isc_result_t
postgres_allowzonexfr(void * driverarg,void * dbdata,const char * name,const char * client)818 postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name,
819 const char *client)
820 {
821 isc_result_t result;
822 PGresult *rs = NULL;
823 unsigned int rows;
824 UNUSED(driverarg);
825
826 /* first check if the zone is supported by the database. */
827 result = postgres_findzone(driverarg, dbdata, name, NULL, NULL);
828 if (result != ISC_R_SUCCESS)
829 return (ISC_R_NOTFOUND);
830
831 /*
832 * if we get to this point we know the zone is supported by
833 * the database the only questions now are is the zone
834 * transfer is allowed for this client and did the config file
835 * have an allow zone xfr query.
836 *
837 * Run our query, and get a result set from the database.
838 */
839 result = postgres_get_resultset(name, NULL, client,
840 ALLOWXFR, dbdata, &rs);
841 /* if we get "not implemented", send it along. */
842 if (result == ISC_R_NOTIMPLEMENTED)
843 return result;
844 /* if we didn't get a result set, log an err msg. */
845 if (result != ISC_R_SUCCESS) {
846 if (rs != NULL)
847 PQclear(rs);
848 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
849 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
850 "Postgres driver unable to return "
851 "result set for allow xfr query");
852 return (ISC_R_FAILURE);
853 }
854 /* count how many rows in result set */
855 rows = PQntuples(rs);
856 /* get rid of result set, we are done with it. */
857 PQclear(rs);
858
859 /* if we returned any rows, zone xfr is allowed. */
860 if (rows > 0)
861 return (ISC_R_SUCCESS);
862
863 /* no rows returned, zone xfr not allowed */
864 return (ISC_R_NOPERM);
865 }
866
867 /*%
868 * If the client is allowed to perform a zone transfer, the next order of
869 * business is to get all the nodes in the zone, so bind can respond to the
870 * query.
871 */
872 static isc_result_t
postgres_allnodes(const char * zone,void * driverarg,void * dbdata,dns_sdlzallnodes_t * allnodes)873 postgres_allnodes(const char *zone, void *driverarg, void *dbdata,
874 dns_sdlzallnodes_t *allnodes)
875 {
876 isc_result_t result;
877 PGresult *rs = NULL;
878 unsigned int i;
879 unsigned int rows;
880 unsigned int fields;
881 unsigned int j;
882 unsigned int len;
883 char *tmpString;
884 char *endp;
885 int ttl;
886
887 UNUSED(driverarg);
888
889 /* run the query and get the result set from the database. */
890 result = postgres_get_resultset(zone, NULL, NULL,
891 ALLNODES, dbdata, &rs);
892 /* if we get "not implemented", send it along */
893 if (result == ISC_R_NOTIMPLEMENTED)
894 return result;
895 /* if we didn't get a result set, log an err msg. */
896 if (result != ISC_R_SUCCESS) {
897 if (rs != NULL)
898 PQclear(rs);
899 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
900 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
901 "Postgres driver unable to return "
902 "result set for all nodes query");
903 return (ISC_R_FAILURE);
904 }
905
906 rows = PQntuples(rs); /* how many rows in result set */
907 fields = PQnfields(rs); /* how many columns in result set */
908 for (i=0; i < rows; i++) {
909 if (fields < 4) { /* gotta have at least 4 columns */
910 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
911 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
912 "Postgres driver too few fields "
913 "returned by all nodes query");
914 }
915 /* convert text to int, make sure it worked right */
916 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
917 if (*endp != '\0' || ttl < 0) {
918 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
919 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
920 "Postgres driver ttl must be "
921 "a postive number");
922 }
923 if (fields == 4) {
924 /* tell Bind about it. */
925 result = dns_sdlz_putnamedrr(allnodes,
926 PQgetvalue(rs, i, 2),
927 PQgetvalue(rs, i, 1),
928 ttl,
929 PQgetvalue(rs, i, 3));
930 } else {
931 /*
932 * more than 4 fields, concatonat the last
933 * ones together. figure out how long to make
934 * string
935 */
936 for (j=3, len=0; j < fields; j++) {
937 len += strlen(PQgetvalue(rs, i, j)) + 1;
938 }
939 /* allocate memory, allow for NULL to term string */
940 tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
941 if (tmpString == NULL) { /* we need more ram. */
942 isc_log_write(dns_lctx,
943 DNS_LOGCATEGORY_DATABASE,
944 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
945 "Postgres driver unable to "
946 "allocate memory for "
947 "temporary string");
948 PQclear(rs);
949 return (ISC_R_FAILURE);
950 }
951 /* copy this field to tmpString */
952 strcpy(tmpString, PQgetvalue(rs, i, 3));
953 /* concatonate the rest, with spaces between */
954 for (j=4; j < fields; j++) {
955 strcat(tmpString, " ");
956 strcat(tmpString, PQgetvalue(rs, i, j));
957 }
958 /* tell Bind about it. */
959 result = dns_sdlz_putnamedrr(allnodes,
960 PQgetvalue(rs, i, 2),
961 PQgetvalue(rs, i, 1),
962 ttl, tmpString);
963 isc_mem_free(ns_g_mctx, tmpString);
964 }
965 /* if we weren't successful, log err msg */
966 if (result != ISC_R_SUCCESS) {
967 PQclear(rs);
968 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
969 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
970 "dns_sdlz_putnamedrr returned error. "
971 "Error code was: %s",
972 isc_result_totext(result));
973 return (ISC_R_FAILURE);
974 }
975 }
976
977 /* free result set memory */
978 PQclear(rs);
979
980 /* if we did return results, we are successful */
981 if (rows > 0)
982 return (ISC_R_SUCCESS);
983
984 /* empty result set, no data found */
985 return (ISC_R_NOTFOUND);
986 }
987
988 /*%
989 * if the lookup function does not return SOA or NS records for the zone,
990 * use this function to get that information for Bind.
991 */
992
993 static isc_result_t
postgres_authority(const char * zone,void * driverarg,void * dbdata,dns_sdlzlookup_t * lookup)994 postgres_authority(const char *zone, void *driverarg, void *dbdata,
995 dns_sdlzlookup_t *lookup)
996 {
997 isc_result_t result;
998 PGresult *rs = NULL;
999
1000 UNUSED(driverarg);
1001
1002 /* run the query and get the result set from the database. */
1003 result = postgres_get_resultset(zone, NULL, NULL,
1004 AUTHORITY, dbdata, &rs);
1005 /* if we get "not implemented", send it along */
1006 if (result == ISC_R_NOTIMPLEMENTED)
1007 return result;
1008 /* if we didn't get a result set, log an err msg. */
1009 if (result != ISC_R_SUCCESS) {
1010 if (rs != NULL)
1011 PQclear(rs);
1012 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1013 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1014 "Postgres driver unable to return "
1015 "result set for authority query");
1016 return (ISC_R_FAILURE);
1017 }
1018 /*
1019 * lookup and authority result sets are processed in the same
1020 * manner postgres_process_rs does the job for both
1021 * functions.
1022 */
1023 return postgres_process_rs(lookup, rs);
1024 }
1025
1026 /*% if zone is supported, lookup up a (or multiple) record(s) in it */
1027 static isc_result_t
postgres_lookup(const char * zone,const char * name,void * driverarg,void * dbdata,dns_sdlzlookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)1028 postgres_lookup(const char *zone, const char *name, void *driverarg,
1029 void *dbdata, dns_sdlzlookup_t *lookup,
1030 dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
1031 {
1032 isc_result_t result;
1033 PGresult *rs = NULL;
1034
1035 UNUSED(driverarg);
1036 UNUSED(methods);
1037 UNUSED(clientinfo);
1038
1039 /* run the query and get the result set from the database. */
1040 result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
1041 /* if we didn't get a result set, log an err msg. */
1042 if (result != ISC_R_SUCCESS) {
1043 if (rs != NULL)
1044 PQclear(rs);
1045 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1046 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1047 "Postgres driver unable to "
1048 "return result set for lookup query");
1049 return (ISC_R_FAILURE);
1050 }
1051 /*
1052 * lookup and authority result sets are processed in the same
1053 * manner postgres_process_rs does the job for both functions.
1054 */
1055 return postgres_process_rs(lookup, rs);
1056 }
1057
1058 /*%
1059 * create an instance of the driver. Remember, only 1 copy of the driver's
1060 * code is ever loaded, the driver has to remember which context it's
1061 * operating in. This is done via use of the dbdata argument which is
1062 * passed into all query functions.
1063 */
1064 static isc_result_t
postgres_create(const char * dlzname,unsigned int argc,char * argv[],void * driverarg,void ** dbdata)1065 postgres_create(const char *dlzname, unsigned int argc, char *argv[],
1066 void *driverarg, void **dbdata)
1067 {
1068 isc_result_t result;
1069 dbinstance_t *dbi = NULL;
1070 unsigned int j;
1071
1072 #ifdef ISC_PLATFORM_USETHREADS
1073 /* if multi-threaded, we need a few extra variables. */
1074 int dbcount;
1075 db_list_t *dblist = NULL;
1076 int i;
1077 char *endp;
1078
1079 #endif /* ISC_PLATFORM_USETHREADS */
1080
1081 UNUSED(driverarg);
1082 UNUSED(dlzname);
1083
1084 /* seed random # generator */
1085 srand( (unsigned)time( NULL ) );
1086
1087
1088 #ifdef ISC_PLATFORM_USETHREADS
1089 /* if debugging, let user know we are multithreaded. */
1090 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1091 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1092 "Postgres driver running multithreaded");
1093 #else /* ISC_PLATFORM_USETHREADS */
1094 /* if debugging, let user know we are single threaded. */
1095 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1096 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1097 "Postgres driver running single threaded");
1098 #endif /* ISC_PLATFORM_USETHREADS */
1099
1100 /* verify we have at least 5 arg's passed to the driver */
1101 if (argc < 5) {
1102 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1103 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1104 "Postgres driver requires at least "
1105 "4 command line args.");
1106 return (ISC_R_FAILURE);
1107 }
1108
1109 /* no more than 8 arg's should be passed to the driver */
1110 if (argc > 8) {
1111 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1112 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1113 "Postgres driver cannot accept more than "
1114 "7 command line args.");
1115 return (ISC_R_FAILURE);
1116 }
1117
1118 /* multithreaded build can have multiple DB connections */
1119 #ifdef ISC_PLATFORM_USETHREADS
1120
1121 /* check how many db connections we should create */
1122 dbcount = strtol(argv[1], &endp, 10);
1123 if (*endp != '\0' || dbcount < 0) {
1124 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1125 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1126 "Postgres driver database connection count "
1127 "must be positive.");
1128 return (ISC_R_FAILURE);
1129 }
1130
1131 /* allocate memory for database connection list */
1132 dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1133 if (dblist == NULL)
1134 return (ISC_R_NOMEMORY);
1135
1136 /* initialize DB connection list */
1137 ISC_LIST_INIT(*dblist);
1138
1139 /*
1140 * create the appropriate number of database instances (DBI)
1141 * append each new DBI to the end of the list
1142 */
1143 for (i=0; i < dbcount; i++) {
1144
1145 #endif /* ISC_PLATFORM_USETHREADS */
1146
1147 /* how many queries were passed in from config file? */
1148 switch(argc) {
1149 case 5:
1150 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1151 NULL, argv[3], argv[4],
1152 NULL, &dbi);
1153 break;
1154 case 6:
1155 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1156 argv[5], argv[3], argv[4],
1157 NULL, &dbi);
1158 break;
1159 case 7:
1160 result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
1161 argv[5], argv[3], argv[4],
1162 NULL, &dbi);
1163 break;
1164 case 8:
1165 result = build_sqldbinstance(ns_g_mctx, argv[6],
1166 argv[7], argv[5], argv[3],
1167 argv[4], NULL, &dbi);
1168 break;
1169 default:
1170 /* not really needed, should shut up compiler. */
1171 result = ISC_R_FAILURE;
1172 }
1173
1174
1175 if (result == ISC_R_SUCCESS) {
1176 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1177 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1178 "Postgres driver created database "
1179 "instance object.");
1180 } else { /* unsuccessful?, log err msg and cleanup. */
1181 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1182 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1183 "Postgres driver could not create "
1184 "database instance object.");
1185 goto cleanup;
1186 }
1187
1188 #ifdef ISC_PLATFORM_USETHREADS
1189
1190 /* when multithreaded, build a list of DBI's */
1191 ISC_LINK_INIT(dbi, link);
1192 ISC_LIST_APPEND(*dblist, dbi, link);
1193
1194 #endif
1195
1196 /* create and set db connection */
1197 dbi->dbconn = PQconnectdb(argv[2]);
1198 /*
1199 * if db connection cannot be created, log err msg and
1200 * cleanup.
1201 */
1202 if (dbi->dbconn == NULL) {
1203 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1204 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1205 "Postgres driver could not allocate "
1206 "memory for database connection");
1207 goto cleanup;
1208 }
1209
1210 /* if we cannot connect the first time, try 3 more times. */
1211 for (j = 0;
1212 PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK &&
1213 j < 3;
1214 j++)
1215 PQreset((PGconn *) dbi->dbconn);
1216
1217
1218 #ifdef ISC_PLATFORM_USETHREADS
1219
1220 /*
1221 * if multi threaded, let user know which connection
1222 * failed. user could be attempting to create 10 db
1223 * connections and for some reason the db backend only
1224 * allows 9
1225 */
1226 if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
1227 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1228 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1229 "Postgres driver failed to create "
1230 "database connection number %u "
1231 "after 4 attempts",
1232 i + 1);
1233 goto cleanup;
1234 }
1235
1236 /* set DBI = null for next loop through. */
1237 dbi = NULL;
1238 } /* end for loop */
1239
1240 /* set dbdata to the list we created. */
1241 *dbdata = dblist;
1242
1243 #else /* ISC_PLATFORM_USETHREADS */
1244 /* if single threaded, just let user know we couldn't connect. */
1245 if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
1246 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1247 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1248 "Postgres driver failed to create database "
1249 "connection after 4 attempts");
1250 goto cleanup;
1251 }
1252
1253 /*
1254 * single threaded build can only use 1 db connection, return
1255 * it via dbdata
1256 */
1257 *dbdata = dbi;
1258
1259 #endif /* ISC_PLATFORM_USETHREADS */
1260
1261 /* hey, we got through all of that ok, return success. */
1262 return(ISC_R_SUCCESS);
1263
1264 cleanup:
1265
1266 #ifdef ISC_PLATFORM_USETHREADS
1267 /*
1268 * if multithreaded, we could fail because only 1 connection
1269 * couldn't be made. We should cleanup the other successful
1270 * connections properly.
1271 */
1272 postgres_destroy_dblist(dblist);
1273
1274 #else /* ISC_PLATFORM_USETHREADS */
1275 if (dbi != NULL)
1276 destroy_sqldbinstance(dbi);
1277
1278 #endif /* ISC_PLATFORM_USETHREADS */
1279 return(ISC_R_FAILURE);
1280 }
1281
1282 /*%
1283 * destroy an instance of the driver. Remember, only 1 copy of the driver's
1284 * code is ever loaded, the driver has to remember which context it's
1285 * operating in. This is done via use of the dbdata argument.
1286 * so we really only need to clean it up since we are not using driverarg.
1287 */
1288 static void
postgres_destroy(void * driverarg,void * dbdata)1289 postgres_destroy(void *driverarg, void *dbdata)
1290 {
1291
1292 #ifdef ISC_PLATFORM_USETHREADS
1293
1294 UNUSED(driverarg);
1295 /* cleanup the list of DBI's */
1296 postgres_destroy_dblist((db_list_t *) dbdata);
1297
1298 #else /* ISC_PLATFORM_USETHREADS */
1299
1300 dbinstance_t *dbi;
1301
1302 UNUSED(driverarg);
1303
1304 dbi = (dbinstance_t *) dbdata;
1305
1306 /* release DB connection */
1307 if (dbi->dbconn != NULL)
1308 PQfinish((PGconn *) dbi->dbconn);
1309
1310 /* destroy single DB instance */
1311 destroy_sqldbinstance(dbi);
1312
1313 #endif /* ISC_PLATFORM_USETHREADS */
1314 }
1315
1316 /* pointers to all our runtime methods. */
1317 /* this is used during driver registration */
1318 /* i.e. in dlz_postgres_init below. */
1319 static dns_sdlzmethods_t dlz_postgres_methods = {
1320 postgres_create,
1321 postgres_destroy,
1322 postgres_findzone,
1323 postgres_lookup,
1324 postgres_authority,
1325 postgres_allnodes,
1326 postgres_allowzonexfr,
1327 NULL,
1328 NULL,
1329 NULL,
1330 NULL,
1331 NULL,
1332 NULL,
1333 NULL,
1334 };
1335
1336 /*%
1337 * Wrapper around dns_sdlzregister().
1338 */
1339 isc_result_t
dlz_postgres_init(void)1340 dlz_postgres_init(void) {
1341 isc_result_t result;
1342
1343 /*
1344 * Write debugging message to log
1345 */
1346 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1347 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1348 "Registering DLZ postgres driver.");
1349
1350 /*
1351 * Driver is always threadsafe. When multithreaded all
1352 * functions use multithreaded code. When not multithreaded,
1353 * all functions can only be entered once, but only 1 thread
1354 * of operation is available in Bind. So everything is still
1355 * threadsafe.
1356 */
1357 result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL,
1358 DNS_SDLZFLAG_RELATIVEOWNER |
1359 DNS_SDLZFLAG_RELATIVERDATA |
1360 DNS_SDLZFLAG_THREADSAFE,
1361 ns_g_mctx, &dlz_postgres);
1362 /* if we can't register the driver, there are big problems. */
1363 if (result != ISC_R_SUCCESS) {
1364 UNEXPECTED_ERROR(__FILE__, __LINE__,
1365 "dns_sdlzregister() failed: %s",
1366 isc_result_totext(result));
1367 result = ISC_R_UNEXPECTED;
1368 }
1369
1370
1371 return result;
1372 }
1373
1374 /*%
1375 * Wrapper around dns_sdlzunregister().
1376 */
1377 void
dlz_postgres_clear(void)1378 dlz_postgres_clear(void) {
1379
1380 /*
1381 * Write debugging message to log
1382 */
1383 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1384 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1385 "Unregistering DLZ postgres driver.");
1386
1387 /* unregister the driver. */
1388 if (dlz_postgres != NULL)
1389 dns_sdlzunregister(&dlz_postgres);
1390 }
1391
1392 #endif
1393