2
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
4
* Permission to use, copy, modify, and distribute this software for any
5
* purpose with or without fee is hereby granted, provided that the
6
* above copyright notice and this permission notice appear in all
9
* THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
10
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
11
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12
* STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
16
* USE OR PERFORMANCE OF THIS SOFTWARE.
18
* The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
19
* conceived and contributed by Rob Butler.
21
* Permission to use, copy, modify, and distribute this software for any
22
* purpose with or without fee is hereby granted, provided that the
23
* above copyright notice and this permission notice appear in all
26
* THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
27
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
28
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
29
* ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
30
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
31
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
32
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
33
* USE OR PERFORMANCE OF THIS SOFTWARE.
37
* Copyright (C) 1999-2001 Internet Software Consortium.
39
* Permission to use, copy, modify, and distribute this software for any
40
* purpose with or without fee is hereby granted, provided that the above
41
* copyright notice and this permission notice appear in all copies.
43
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
44
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
45
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
46
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
47
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
48
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
49
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
50
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
62
#include <dns/result.h>
65
#include <isc/platform.h>
66
#include <isc/print.h>
67
#include <isc/result.h>
68
#include <isc/string.h>
71
#include <named/globals.h>
73
#include <dlz/sdlz_helper.h>
74
#include <dlz/dlz_postgres_driver.h>
76
/* temporarily include time. */
81
static dns_sdlzimplementation_t *dlz_postgres = NULL;
83
#define dbc_search_limit 30
95
* Escaping arbitrary strings to get valid SQL strings/identifiers.
97
* Replaces "\\" with "\\\\" and "'" with "''".
98
* length is the length of the buffer pointed to by
99
* from. The buffer at to must be at least 2*length + 1 characters
100
* long. A terminating NUL character is written.
103
* This function was borrowed directly from PostgreSQL's libpq.
104
* The function was originally called PQescapeString and renamed
105
* to postgres_makesafe to avoid a naming collision.
106
* PQescapeString is a new function made available in Postgres 7.2.
107
* For some reason the function is not properly exported on Win32
108
* builds making the function unavailable on Windows. Also, since
109
* this function is new it would require building this driver with
110
* the libpq 7.2. By borrowing this function the Windows problem
111
* is solved, and the dependence on libpq 7.2 is removed. Libpq is
112
* still required of course, but an older version should work now too.
114
* The copyright statements from the original file containing this
115
* function are included below:
116
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
117
* Portions Copyright (c) 1994, Regents of the University of California
122
postgres_makesafe(char *to, const char *from, size_t length)
124
const char *source = from;
126
unsigned int remaining = length;
128
while (remaining > 0)
136
/* target and remaining are updated below. */
143
/* target and remaining are updated below. */
148
/* target and remaining are updated below. */
155
/* Write the terminating NUL character. */
161
#ifdef ISC_PLATFORM_USETHREADS
164
* Properly cleans up a list of database instances.
165
* This function is only used when the driver is compiled for
166
* multithreaded operation.
169
postgres_destroy_dblist(db_list_t *dblist)
172
dbinstance_t *ndbi = NULL;
173
dbinstance_t *dbi = NULL;
175
/* get the first DBI in the list */
176
ndbi = ISC_LIST_HEAD(*dblist);
178
/* loop through the list */
179
while (ndbi != NULL) {
181
/* get the next DBI in the list */
182
ndbi = ISC_LIST_NEXT(dbi, link);
183
/* release DB connection */
184
if (dbi->dbconn != NULL)
185
PQfinish((PGconn *) dbi->dbconn);
186
/* release all memory that comprised a DBI */
187
destroy_sqldbinstance(dbi);
189
/* release memory for the list structure */
190
isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
194
* Loops through the list of DB instances, attempting to lock
195
* on the mutex. If successful, the DBI is reserved for use
196
* and the thread can perform queries against the database.
197
* If the lock fails, the next one in the list is tried.
198
* looping continues until a lock is obtained, or until
199
* the list has been searched dbc_search_limit times.
200
* This function is only used when the driver is compiled for
201
* multithreaded operation.
204
static dbinstance_t *
205
postgres_find_avail_conn(db_list_t *dblist)
207
dbinstance_t *dbi = NULL;
211
/* get top of list */
212
head = dbi = ISC_LIST_HEAD(*dblist);
214
/* loop through list */
215
while (count < dbc_search_limit) {
216
/* try to lock on the mutex */
217
if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
218
return dbi; /* success, return the DBI for use. */
220
/* not successful, keep trying */
221
dbi = ISC_LIST_NEXT(dbi, link);
223
/* check to see if we have gone to the top of the list. */
229
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
230
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
231
"Postgres driver unable to find available connection "
232
"after searching %d times",
237
#endif /* ISC_PLATFORM_USETHREADS */
240
* Allocates memory for a new string, and then constructs the new
241
* string by "escaping" the input string. The new string is
242
* safe to be used in queries. This is necessary because we cannot
243
* be sure of what types of strings are passed to us, and we don't
244
* want special characters in the string causing problems.
248
postgres_escape_string(const char *instr) {
258
outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
262
postgres_makesafe(outstr, instr, len);
263
/* PQescapeString(outstr, instr, len); */
269
* This function is the real core of the driver. Zone, record
270
* and client strings are passed in (or NULL is passed if the
271
* string is not available). The type of query we want to run
272
* is indicated by the query flag, and the dbdata object is passed
273
* passed in to. dbdata really holds either:
274
* 1) a list of database instances (in multithreaded mode) OR
275
* 2) a single database instance (in single threaded mode)
276
* The function will construct the query and obtain an available
277
* database instance (DBI). It will then run the query and hopefully
278
* obtain a result set. Postgres is nice, in that once the result
279
* set is returned, we can make the db connection available for another
280
* thread to use, while this thread continues on. So, the DBI is made
281
* available ASAP by unlocking the instance_lock after we have cleaned
285
postgres_get_resultset(const char *zone, const char *record,
286
const char *client, unsigned int query,
287
void *dbdata, PGresult **rs)
290
dbinstance_t *dbi = NULL;
291
char *querystring = NULL;
295
/* temporarily get a unique thread # */
296
unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));
298
REQUIRE(*rs == NULL);
301
/* temporary logging message */
302
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
303
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
304
"%d Getting DBI", dlz_thread_num);
307
/* get db instance / connection */
308
#ifdef ISC_PLATFORM_USETHREADS
310
/* find an available DBI from the list */
311
dbi = postgres_find_avail_conn((db_list_t *) dbdata);
313
#else /* ISC_PLATFORM_USETHREADS */
316
* only 1 DBI - no need to lock instance lock either
317
* only 1 thread in the whole process, no possible contention.
319
dbi = (dbinstance_t *) dbdata;
321
#endif /* ISC_PLATFORM_USETHREADS */
324
/* temporary logging message */
325
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
326
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
327
"%d Got DBI - checking query", dlz_thread_num);
330
/* if DBI is null, can't do anything else */
332
result = ISC_R_FAILURE;
336
/* what type of query are we going to run? */
340
* if the query was not passed in from the config file
341
* then we can't run it. return not_implemented, so
342
* it's like the code for that operation was never
343
* built into the driver.... AHHH flexibility!!!
345
if (dbi->allnodes_q == NULL) {
346
result = ISC_R_NOTIMPLEMENTED;
351
/* same as comments as ALLNODES */
352
if (dbi->allowxfr_q == NULL) {
353
result = ISC_R_NOTIMPLEMENTED;
358
/* same as comments as ALLNODES */
359
if (dbi->authority_q == NULL) {
360
result = ISC_R_NOTIMPLEMENTED;
365
/* this is required. It's the whole point of DLZ! */
366
if (dbi->findzone_q == NULL) {
367
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
368
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
369
"No query specified for findzone. "
370
"Findzone requires a query");
371
result = ISC_R_FAILURE;
376
/* this is required. It's also a major point of DLZ! */
377
if (dbi->lookup_q == NULL) {
378
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
379
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
380
"No query specified for lookup. "
381
"Lookup requires a query");
382
result = ISC_R_FAILURE;
388
* this should never happen. If it does, the code is
391
UNEXPECTED_ERROR(__FILE__, __LINE__,
392
"Incorrect query flag passed to "
393
"postgres_get_resultset");
394
result = ISC_R_UNEXPECTED;
399
/* temporary logging message */
400
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
401
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
402
"%d checked query", dlz_thread_num);
406
* was a zone string passed? If so, make it safe for use in
410
dbi->zone = postgres_escape_string(zone);
411
if (dbi->zone == NULL) {
412
result = ISC_R_NOMEMORY;
415
} else { /* no string passed, set the string pointer to NULL */
420
/* temporary logging message */
421
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
422
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
423
"%d did zone", dlz_thread_num);
427
* was a record string passed? If so, make it safe for use in
430
if (record != NULL) {
431
dbi->record = postgres_escape_string(record);
432
if (dbi->record == NULL) {
433
result = ISC_R_NOMEMORY;
436
} else { /* no string passed, set the string pointer to NULL */
442
/* temporary logging message */
443
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
444
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
445
"%d did record", dlz_thread_num);
449
* was a client string passed? If so, make it safe for use in
452
if (client != NULL) {
453
dbi->client = postgres_escape_string(client);
454
if (dbi->client == NULL) {
455
result = ISC_R_NOMEMORY;
458
} else { /* no string passed, set the string pointer to NULL */
463
/* temporary logging message */
464
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
465
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
466
"%d did client", dlz_thread_num);
470
* what type of query are we going to run?
471
* this time we build the actual query to run.
475
querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
478
querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
481
querystring = build_querystring(ns_g_mctx, dbi->authority_q);
484
querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
487
querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
491
* this should never happen. If it does, the code is
494
UNEXPECTED_ERROR(__FILE__, __LINE__,
495
"Incorrect query flag passed to "
496
"postgres_get_resultset");
497
result = ISC_R_UNEXPECTED;
502
/* temporary logging message */
503
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
504
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
505
"%d built query", dlz_thread_num);
508
/* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
509
if (querystring == NULL) {
510
result = ISC_R_NOMEMORY;
515
/* temporary logging message */
516
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
517
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
518
"%d query is '%s'", dlz_thread_num, querystring);
522
* output the full query string during debug so we can see
523
* what lame error the query has.
525
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
526
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
527
"\nQuery String: %s\n", querystring);
529
/* attempt query up to 3 times. */
530
for (j=0; j < 3; j++) {
532
/* temporary logging message */
533
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
534
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
535
"%d executing query for %d time",
538
/* try to get result set */
539
*rs = PQexec((PGconn *)dbi->dbconn, querystring );
540
result = ISC_R_SUCCESS;
542
* if result set is null, reset DB connection, max 3
545
for (i=0; *rs == NULL && i < 3; i++) {
547
/* temporary logging message */
548
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
549
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
550
"%d resetting connection",
553
result = ISC_R_FAILURE;
554
PQreset((PGconn *) dbi->dbconn);
555
/* connection ok, break inner loop */
556
if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK)
559
/* result set ok, break outter loop */
560
if (PQresultStatus(*rs) == PGRES_TUPLES_OK) {
562
/* temporary logging message */
563
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
564
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
565
"%d rs ok", dlz_thread_num);
569
/* we got a result set object, but it's not right. */
571
/* temporary logging message */
572
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
573
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
574
"%d clearing rs", dlz_thread_num);
576
PQclear(*rs); /* get rid of it */
577
/* in case this was the last attempt */
578
result = ISC_R_FAILURE;
583
/* it's always good to cleanup after yourself */
586
/* temporary logging message */
587
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
588
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
589
"%d cleaning up", dlz_thread_num);
592
/* if we couldn't even allocate DBI, just return NULL */
594
return ISC_R_FAILURE;
596
/* free dbi->zone string */
597
if (dbi->zone != NULL)
598
isc_mem_free(ns_g_mctx, dbi->zone);
600
/* free dbi->record string */
601
if (dbi->record != NULL)
602
isc_mem_free(ns_g_mctx, dbi->record);
604
/* free dbi->client string */
605
if (dbi->client != NULL)
606
isc_mem_free(ns_g_mctx, dbi->client);
608
#ifdef ISC_PLATFORM_USETHREADS
611
/* temporary logging message */
612
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
613
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
614
"%d unlocking mutex", dlz_thread_num);
617
/* release the lock so another thread can use this dbi */
618
isc_mutex_unlock(&dbi->instance_lock);
620
#endif /* ISC_PLATFORM_USETHREADS */
622
/* release query string */
623
if (querystring != NULL)
624
isc_mem_free(ns_g_mctx, querystring );
627
/* temporary logging message */
628
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
629
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
630
"%d returning", dlz_thread_num);
638
* The processing of result sets for lookup and authority are
639
* exactly the same. So that functionality has been moved
640
* into this function to minimize code.
644
postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs)
656
rows = PQntuples(rs); /* how many rows in result set */
657
fields = PQnfields(rs); /* how many columns in result set */
658
for (i=0; i < rows; i++) {
662
* one column in rs, it's the data field. use
663
* default type of A record, and default TTL
666
result = dns_sdlz_putrr(lookup, "a", 86400,
667
PQgetvalue(rs, i, 0));
670
/* two columns, data field, and data type.
671
* use default TTL of 86400.
673
result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0),
674
86400, PQgetvalue(rs, i, 1));
677
/* three columns, all data no defaults.
678
* convert text to int, make sure it worked
681
ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
682
if (*endp != '\0' || ttl < 0) {
683
isc_log_write(dns_lctx,
684
DNS_LOGCATEGORY_DATABASE,
685
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
686
"Postgres driver ttl must be "
687
"a positive number");
689
result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
690
ttl, PQgetvalue(rs, i, 2));
694
* more than 3 fields, concatenate the last
695
* ones together. figure out how long to make
698
for (j=2, len=0; j < fields; j++) {
699
len += strlen(PQgetvalue(rs, i, j)) + 1;
702
* allocate string memory, allow for NULL to
705
tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
706
if (tmpString == NULL) {
707
/* major bummer, need more ram */
708
isc_log_write(dns_lctx,
709
DNS_LOGCATEGORY_DATABASE,
710
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
711
"Postgres driver unable to "
712
"allocate memory for "
715
return (ISC_R_FAILURE); /* Yeah, I'd say! */
717
/* copy field to tmpString */
718
strcpy(tmpString, PQgetvalue(rs, i, 2));
720
* concat the rest of fields together, space
723
for (j=3; j < fields; j++) {
724
strcat(tmpString, " ");
725
strcat(tmpString, PQgetvalue(rs, i, j));
727
/* convert text to int, make sure it worked right */
728
ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
729
if (*endp != '\0' || ttl < 0) {
730
isc_log_write(dns_lctx,
731
DNS_LOGCATEGORY_DATABASE,
732
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
733
"Postgres driver ttl must be "
736
/* ok, now tell Bind about it. */
737
result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
739
/* done, get rid of this thing. */
740
isc_mem_free(ns_g_mctx, tmpString);
742
/* I sure hope we were successful */
743
if (result != ISC_R_SUCCESS) {
744
/* nope, get rid of the Result set, and log a msg */
746
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
747
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
748
"dns_sdlz_putrr returned error. "
749
"Error code was: %s",
750
isc_result_totext(result));
751
return (ISC_R_FAILURE);
755
/* free result set memory */
758
/* if we did return results, we are successful */
760
return (ISC_R_SUCCESS);
762
/* empty result set, no data found */
763
return (ISC_R_NOTFOUND);
767
* SDLZ interface methods
770
/*% determine if the zone is supported by (in) the database */
773
postgres_findzone(void *driverarg, void *dbdata, const char *name)
780
/* run the query and get the result set from the database. */
781
result = postgres_get_resultset(name, NULL, NULL,
782
FINDZONE, dbdata, &rs);
783
/* if we didn't get a result set, log an err msg. */
784
if (result != ISC_R_SUCCESS) {
787
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
788
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
789
"Postgres driver unable to return "
790
"result set for findzone query");
791
return (ISC_R_FAILURE);
793
/* count how many rows in result set */
794
rows = PQntuples(rs);
795
/* get rid of result set, we are done with it. */
798
/* if we returned any rows, zone is supported. */
800
return (ISC_R_SUCCESS);
802
/* no rows returned, zone is not supported. */
803
return (ISC_R_NOTFOUND);
806
/*% Determine if the client is allowed to perform a zone transfer */
808
postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name,
816
/* first check if the zone is supported by the database. */
817
result = postgres_findzone(driverarg, dbdata, name);
818
if (result != ISC_R_SUCCESS)
819
return (ISC_R_NOTFOUND);
822
* if we get to this point we know the zone is supported by
823
* the database the only questions now are is the zone
824
* transfer is allowed for this client and did the config file
825
* have an allow zone xfr query.
827
* Run our query, and get a result set from the database.
829
result = postgres_get_resultset(name, NULL, client,
830
ALLOWXFR, dbdata, &rs);
831
/* if we get "not implemented", send it along. */
832
if (result == ISC_R_NOTIMPLEMENTED)
834
/* if we didn't get a result set, log an err msg. */
835
if (result != ISC_R_SUCCESS) {
838
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
839
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
840
"Postgres driver unable to return "
841
"result set for allow xfr query");
842
return (ISC_R_FAILURE);
844
/* count how many rows in result set */
845
rows = PQntuples(rs);
846
/* get rid of result set, we are done with it. */
849
/* if we returned any rows, zone xfr is allowed. */
851
return (ISC_R_SUCCESS);
853
/* no rows returned, zone xfr not allowed */
854
return (ISC_R_NOPERM);
858
* If the client is allowed to perform a zone transfer, the next order of
859
* business is to get all the nodes in the zone, so bind can respond to the
863
postgres_allnodes(const char *zone, void *driverarg, void *dbdata,
864
dns_sdlzallnodes_t *allnodes)
879
/* run the query and get the result set from the database. */
880
result = postgres_get_resultset(zone, NULL, NULL,
881
ALLNODES, dbdata, &rs);
882
/* if we get "not implemented", send it along */
883
if (result == ISC_R_NOTIMPLEMENTED)
885
/* if we didn't get a result set, log an err msg. */
886
if (result != ISC_R_SUCCESS) {
889
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
890
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
891
"Postgres driver unable to return "
892
"result set for all nodes query");
893
return (ISC_R_FAILURE);
896
rows = PQntuples(rs); /* how many rows in result set */
897
fields = PQnfields(rs); /* how many columns in result set */
898
for (i=0; i < rows; i++) {
899
if (fields < 4) { /* gotta have at least 4 columns */
900
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
901
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
902
"Postgres driver too few fields "
903
"returned by all nodes query");
905
/* convert text to int, make sure it worked right */
906
ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
907
if (*endp != '\0' || ttl < 0) {
908
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
909
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
910
"Postgres driver ttl must be "
914
/* tell Bind about it. */
915
result = dns_sdlz_putnamedrr(allnodes,
916
PQgetvalue(rs, i, 2),
917
PQgetvalue(rs, i, 1),
919
PQgetvalue(rs, i, 3));
922
* more than 4 fields, concatonat the last
923
* ones together. figure out how long to make
926
for (j=3, len=0; j < fields; j++) {
927
len += strlen(PQgetvalue(rs, i, j)) + 1;
929
/* allocate memory, allow for NULL to term string */
930
tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
931
if (tmpString == NULL) { /* we need more ram. */
932
isc_log_write(dns_lctx,
933
DNS_LOGCATEGORY_DATABASE,
934
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
935
"Postgres driver unable to "
936
"allocate memory for "
939
return (ISC_R_FAILURE);
941
/* copy this field to tmpString */
942
strcpy(tmpString, PQgetvalue(rs, i, 3));
943
/* concatonate the rest, with spaces between */
944
for (j=4; j < fields; j++) {
945
strcat(tmpString, " ");
946
strcat(tmpString, PQgetvalue(rs, i, j));
948
/* tell Bind about it. */
949
result = dns_sdlz_putnamedrr(allnodes,
950
PQgetvalue(rs, i, 2),
951
PQgetvalue(rs, i, 1),
953
isc_mem_free(ns_g_mctx, tmpString);
955
/* if we weren't successful, log err msg */
956
if (result != ISC_R_SUCCESS) {
958
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
959
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
960
"dns_sdlz_putnamedrr returned error. "
961
"Error code was: %s",
962
isc_result_totext(result));
963
return (ISC_R_FAILURE);
967
/* free result set memory */
970
/* if we did return results, we are successful */
972
return (ISC_R_SUCCESS);
974
/* empty result set, no data found */
975
return (ISC_R_NOTFOUND);
979
* if the lookup function does not return SOA or NS records for the zone,
980
* use this function to get that information for Bind.
984
postgres_authority(const char *zone, void *driverarg, void *dbdata,
985
dns_sdlzlookup_t *lookup)
992
/* run the query and get the result set from the database. */
993
result = postgres_get_resultset(zone, NULL, NULL,
994
AUTHORITY, dbdata, &rs);
995
/* if we get "not implemented", send it along */
996
if (result == ISC_R_NOTIMPLEMENTED)
998
/* if we didn't get a result set, log an err msg. */
999
if (result != ISC_R_SUCCESS) {
1002
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1003
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1004
"Postgres driver unable to return "
1005
"result set for authority query");
1006
return (ISC_R_FAILURE);
1009
* lookup and authority result sets are processed in the same
1010
* manner postgres_process_rs does the job for both
1013
return postgres_process_rs(lookup, rs);
1016
/*% if zone is supported, lookup up a (or multiple) record(s) in it */
1018
postgres_lookup(const char *zone, const char *name, void *driverarg,
1019
void *dbdata, dns_sdlzlookup_t *lookup)
1021
isc_result_t result;
1022
PGresult *rs = NULL;
1026
/* run the query and get the result set from the database. */
1027
result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
1028
/* if we didn't get a result set, log an err msg. */
1029
if (result != ISC_R_SUCCESS) {
1032
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1033
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1034
"Postgres driver unable to "
1035
"return result set for lookup query");
1036
return (ISC_R_FAILURE);
1039
* lookup and authority result sets are processed in the same
1040
* manner postgres_process_rs does the job for both functions.
1042
return postgres_process_rs(lookup, rs);
1046
* create an instance of the driver. Remember, only 1 copy of the driver's
1047
* code is ever loaded, the driver has to remember which context it's
1048
* operating in. This is done via use of the dbdata argument which is
1049
* passed into all query functions.
1052
postgres_create(const char *dlzname, unsigned int argc, char *argv[],
1053
void *driverarg, void **dbdata)
1055
isc_result_t result;
1056
dbinstance_t *dbi = NULL;
1059
#ifdef ISC_PLATFORM_USETHREADS
1060
/* if multi-threaded, we need a few extra variables. */
1062
db_list_t *dblist = NULL;
1066
#endif /* ISC_PLATFORM_USETHREADS */
1071
/* seed random # generator */
1072
srand( (unsigned)time( NULL ) );
1075
#ifdef ISC_PLATFORM_USETHREADS
1076
/* if debugging, let user know we are multithreaded. */
1077
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1078
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1079
"Postgres driver running multithreaded");
1080
#else /* ISC_PLATFORM_USETHREADS */
1081
/* if debugging, let user know we are single threaded. */
1082
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1083
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1084
"Postgres driver running single threaded");
1085
#endif /* ISC_PLATFORM_USETHREADS */
1087
/* verify we have at least 5 arg's passed to the driver */
1089
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1090
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1091
"Postgres driver requires at least "
1092
"4 command line args.");
1093
return (ISC_R_FAILURE);
1096
/* no more than 8 arg's should be passed to the driver */
1098
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1099
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1100
"Postgres driver cannot accept more than "
1101
"7 command line args.");
1102
return (ISC_R_FAILURE);
1105
/* multithreaded build can have multiple DB connections */
1106
#ifdef ISC_PLATFORM_USETHREADS
1108
/* check how many db connections we should create */
1109
dbcount = strtol(argv[1], &endp, 10);
1110
if (*endp != '\0' || dbcount < 0) {
1111
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1112
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1113
"Postgres driver database connection count "
1114
"must be positive.");
1115
return (ISC_R_FAILURE);
1118
/* allocate memory for database connection list */
1119
dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1121
return (ISC_R_NOMEMORY);
1123
/* initialize DB connection list */
1124
ISC_LIST_INIT(*dblist);
1127
* create the appropriate number of database instances (DBI)
1128
* append each new DBI to the end of the list
1130
for (i=0; i < dbcount; i++) {
1132
#endif /* ISC_PLATFORM_USETHREADS */
1134
/* how many queries were passed in from config file? */
1137
result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1138
NULL, argv[3], argv[4],
1142
result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1143
argv[5], argv[3], argv[4],
1147
result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
1148
argv[5], argv[3], argv[4],
1152
result = build_sqldbinstance(ns_g_mctx, argv[6],
1153
argv[7], argv[5], argv[3],
1154
argv[4], NULL, &dbi);
1157
/* not really needed, should shut up compiler. */
1158
result = ISC_R_FAILURE;
1162
if (result == ISC_R_SUCCESS) {
1163
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1164
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1165
"Postgres driver created database "
1166
"instance object.");
1167
} else { /* unsuccessful?, log err msg and cleanup. */
1168
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1169
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1170
"Postgres driver could not create "
1171
"database instance object.");
1175
#ifdef ISC_PLATFORM_USETHREADS
1177
/* when multithreaded, build a list of DBI's */
1178
ISC_LINK_INIT(dbi, link);
1179
ISC_LIST_APPEND(*dblist, dbi, link);
1183
/* create and set db connection */
1184
dbi->dbconn = PQconnectdb(argv[2]);
1186
* if db connection cannot be created, log err msg and
1189
if (dbi->dbconn == NULL) {
1190
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1191
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1192
"Postgres driver could not allocate "
1193
"memory for database connection");
1197
/* if we cannot connect the first time, try 3 more times. */
1199
PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK &&
1202
PQreset((PGconn *) dbi->dbconn);
1205
#ifdef ISC_PLATFORM_USETHREADS
1208
* if multi threaded, let user know which connection
1209
* failed. user could be attempting to create 10 db
1210
* connections and for some reason the db backend only
1213
if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
1214
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1215
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1216
"Postgres driver failed to create "
1217
"database connection number %u "
1223
/* set DBI = null for next loop through. */
1225
} /* end for loop */
1227
/* set dbdata to the list we created. */
1230
#else /* ISC_PLATFORM_USETHREADS */
1231
/* if single threaded, just let user know we couldn't connect. */
1232
if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
1233
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1234
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1235
"Postgres driver failed to create database "
1236
"connection after 4 attempts");
1241
* single threaded build can only use 1 db connection, return
1246
#endif /* ISC_PLATFORM_USETHREADS */
1248
/* hey, we got through all of that ok, return success. */
1249
return(ISC_R_SUCCESS);
1253
#ifdef ISC_PLATFORM_USETHREADS
1255
* if multithreaded, we could fail because only 1 connection
1256
* couldn't be made. We should cleanup the other successful
1257
* connections properly.
1259
postgres_destroy_dblist(dblist);
1261
#else /* ISC_PLATFORM_USETHREADS */
1263
destroy_sqldbinstance(dbi);
1265
#endif /* ISC_PLATFORM_USETHREADS */
1266
return(ISC_R_FAILURE);
1270
* destroy an instance of the driver. Remember, only 1 copy of the driver's
1271
* code is ever loaded, the driver has to remember which context it's
1272
* operating in. This is done via use of the dbdata argument.
1273
* so we really only need to clean it up since we are not using driverarg.
1276
postgres_destroy(void *driverarg, void *dbdata)
1279
#ifdef ISC_PLATFORM_USETHREADS
1282
/* cleanup the list of DBI's */
1283
postgres_destroy_dblist((db_list_t *) dbdata);
1285
#else /* ISC_PLATFORM_USETHREADS */
1291
dbi = (dbinstance_t *) dbdata;
1293
/* release DB connection */
1294
if (dbi->dbconn != NULL)
1295
PQfinish((PGconn *) dbi->dbconn);
1297
/* destroy single DB instance */
1298
destroy_sqldbinstance(dbi);
1300
#endif /* ISC_PLATFORM_USETHREADS */
1303
/* pointers to all our runtime methods. */
1304
/* this is used during driver registration */
1305
/* i.e. in dlz_postgres_init below. */
1306
static dns_sdlzmethods_t dlz_postgres_methods = {
1313
postgres_allowzonexfr
1317
* Wrapper around dns_sdlzregister().
1320
dlz_postgres_init(void) {
1321
isc_result_t result;
1324
* Write debugging message to log
1326
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1327
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1328
"Registering DLZ postgres driver.");
1331
* Driver is always threadsafe. When multithreaded all
1332
* functions use multithreaded code. When not multithreaded,
1333
* all functions can only be entered once, but only 1 thread
1334
* of operation is available in Bind. So everything is still
1337
result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL,
1338
DNS_SDLZFLAG_RELATIVEOWNER |
1339
DNS_SDLZFLAG_RELATIVERDATA |
1340
DNS_SDLZFLAG_THREADSAFE,
1341
ns_g_mctx, &dlz_postgres);
1342
/* if we can't register the driver, there are big problems. */
1343
if (result != ISC_R_SUCCESS) {
1344
UNEXPECTED_ERROR(__FILE__, __LINE__,
1345
"dns_sdlzregister() failed: %s",
1346
isc_result_totext(result));
1347
result = ISC_R_UNEXPECTED;
1355
* Wrapper around dns_sdlzunregister().
1358
dlz_postgres_clear(void) {
1361
* Write debugging message to log
1363
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1364
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1365
"Unregistering DLZ postgres driver.");
1367
/* unregister the driver. */
1368
if (dlz_postgres != NULL)
1369
dns_sdlzunregister(&dlz_postgres);