~ubuntu-branches/ubuntu/maverick/bind9/maverick

« back to all changes in this revision

Viewing changes to contrib/dlz/drivers/dlz_postgres_driver.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones, LaMont Jones, Internet Software Consortium, Inc, localization folks
  • Date: 2008-08-02 14:20:20 UTC
  • mfrom: (1.2.1 upstream) (6.1.24 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080802142020-l1hon9jy8lbbjxmg
[LaMont Jones]

* default to using resolvconf if it is installed
* fix sonames and dependencies.  Closes: #149259, #492418
* Do not build-depend libcap2-dev on non-linux.  Closes: #493392
* drop unused query-loc manpage.  Closes: #492564
* lwresd: Deliver /etc/bind directory.  Closes: #490027
* fix query-source comment in default install

[Internet Software Consortium, Inc]

* 9.5.0-P2.  Closes: #492949

[localization folks]

* l10n: Spanish debconf translation.  Closes: #492425 (Ignacio Mondino)
* l10n: Swedish debconf templates.  Closes: #491369 (Martin Ågren)
* l10n: Japanese debconf translations.  Closes: #492048 (Hideki Yamane
  (Debian-JP))
* l10n: Finnish translation.  Closes: #490630 (Esko Arajärvi)
* l10n: Italian debconf translations.  Closes: #492587 (Alessandro Vietta)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
 
3
 *
 
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
 
7
 * copies.
 
8
 *
 
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.
 
17
 *
 
18
 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
 
19
 * conceived and contributed by Rob Butler.
 
20
 *
 
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
 
24
 * copies.
 
25
 *
 
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.
 
34
 */
 
35
 
 
36
/*
 
37
 * Copyright (C) 1999-2001  Internet Software Consortium.
 
38
 *
 
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.
 
42
 *
 
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.
 
51
 */
 
52
 
 
53
#ifdef DLZ_POSTGRES
 
54
 
 
55
#include <config.h>
 
56
#include <stdio.h>
 
57
#include <string.h>
 
58
#include <stdlib.h>
 
59
 
 
60
#include <dns/log.h>
 
61
#include <dns/sdlz.h>
 
62
#include <dns/result.h>
 
63
 
 
64
#include <isc/mem.h>
 
65
#include <isc/platform.h>
 
66
#include <isc/print.h>
 
67
#include <isc/result.h>
 
68
#include <isc/string.h>
 
69
#include <isc/util.h>
 
70
 
 
71
#include <named/globals.h>
 
72
 
 
73
#include <dlz/sdlz_helper.h>
 
74
#include <dlz/dlz_postgres_driver.h>
 
75
 
 
76
/* temporarily include time. */
 
77
#include <time.h>
 
78
 
 
79
#include <libpq-fe.h>
 
80
 
 
81
static dns_sdlzimplementation_t *dlz_postgres = NULL;
 
82
 
 
83
#define dbc_search_limit 30
 
84
#define ALLNODES 1
 
85
#define ALLOWXFR 2
 
86
#define AUTHORITY 3
 
87
#define FINDZONE 4
 
88
#define LOOKUP 5
 
89
 
 
90
/*
 
91
 * Private methods
 
92
 */
 
93
 
 
94
/* ---------------
 
95
 * Escaping arbitrary strings to get valid SQL strings/identifiers.
 
96
 *
 
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.
 
101
 *
 
102
 * NOTICE!!!
 
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.
 
113
 *
 
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
 
118
 * ---------------
 
119
 */
 
120
 
 
121
static size_t
 
122
postgres_makesafe(char *to, const char *from, size_t length)
 
123
{
 
124
        const char *source = from;
 
125
        char       *target = to;
 
126
        unsigned int remaining = length;
 
127
 
 
128
        while (remaining > 0)
 
129
        {
 
130
                switch (*source)
 
131
                {
 
132
                case '\\':
 
133
                        *target = '\\';
 
134
                        target++;
 
135
                        *target = '\\';
 
136
                        /* target and remaining are updated below. */
 
137
                        break;
 
138
 
 
139
                case '\'':
 
140
                        *target = '\'';
 
141
                        target++;
 
142
                        *target = '\'';
 
143
                        /* target and remaining are updated below. */
 
144
                        break;
 
145
 
 
146
                default:
 
147
                        *target = *source;
 
148
                        /* target and remaining are updated below. */
 
149
                }
 
150
                source++;
 
151
                target++;
 
152
                remaining--;
 
153
        }
 
154
 
 
155
        /* Write the terminating NUL character. */
 
156
        *target = '\0';
 
157
 
 
158
        return target - to;
 
159
}
 
160
 
 
161
#ifdef ISC_PLATFORM_USETHREADS
 
162
 
 
163
/*%
 
164
 * Properly cleans up a list of database instances.
 
165
 * This function is only used when the driver is compiled for
 
166
 * multithreaded operation.
 
167
 */
 
168
static void
 
169
postgres_destroy_dblist(db_list_t *dblist)
 
170
{
 
171
 
 
172
        dbinstance_t *ndbi = NULL;
 
173
        dbinstance_t *dbi = NULL;
 
174
 
 
175
        /* get the first DBI in the list */
 
176
        ndbi = ISC_LIST_HEAD(*dblist);
 
177
 
 
178
        /* loop through the list */
 
179
        while (ndbi != NULL) {
 
180
                dbi = ndbi;
 
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);
 
188
        }
 
189
        /* release memory for the list structure */
 
190
        isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
 
191
}
 
192
 
 
193
/*%
 
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.
 
202
 */
 
203
 
 
204
static dbinstance_t *
 
205
postgres_find_avail_conn(db_list_t *dblist)
 
206
{
 
207
        dbinstance_t *dbi = NULL;
 
208
        dbinstance_t *head;
 
209
        int count = 0;
 
210
 
 
211
        /* get top of list */
 
212
        head = dbi = ISC_LIST_HEAD(*dblist);
 
213
 
 
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. */
 
219
 
 
220
                /* not successful, keep trying */
 
221
                dbi = ISC_LIST_NEXT(dbi, link);
 
222
 
 
223
                /* check to see if we have gone to the top of the list. */
 
224
                if (dbi == NULL) {
 
225
                        count++;
 
226
                        dbi = head;
 
227
                }
 
228
        }
 
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",
 
233
                      count);
 
234
        return NULL;
 
235
}
 
236
 
 
237
#endif /* ISC_PLATFORM_USETHREADS */
 
238
 
 
239
/*%
 
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.
 
245
 */
 
246
 
 
247
static char *
 
248
postgres_escape_string(const char *instr) {
 
249
 
 
250
        char *outstr;
 
251
        unsigned int len;
 
252
 
 
253
        if (instr == NULL)
 
254
                return NULL;
 
255
 
 
256
        len = strlen(instr);
 
257
 
 
258
        outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
 
259
        if (outstr == NULL)
 
260
                return NULL;
 
261
 
 
262
        postgres_makesafe(outstr, instr, len);
 
263
        /* PQescapeString(outstr, instr, len); */
 
264
 
 
265
        return outstr;
 
266
}
 
267
 
 
268
/*%
 
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
 
282
 * it up properly.
 
283
 */
 
284
static isc_result_t
 
285
postgres_get_resultset(const char *zone, const char *record,
 
286
                       const char *client, unsigned int query,
 
287
                       void *dbdata, PGresult **rs)
 
288
{
 
289
        isc_result_t result;
 
290
        dbinstance_t *dbi = NULL;
 
291
        char *querystring = NULL;
 
292
        unsigned int i = 0;
 
293
        unsigned int j = 0;
 
294
 
 
295
        /* temporarily get a unique thread # */
 
296
        unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));
 
297
 
 
298
        REQUIRE(*rs == NULL);
 
299
 
 
300
#if 0
 
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);
 
305
#endif
 
306
 
 
307
        /* get db instance / connection */
 
308
#ifdef ISC_PLATFORM_USETHREADS
 
309
 
 
310
        /* find an available DBI from the list */
 
311
        dbi = postgres_find_avail_conn((db_list_t *) dbdata);
 
312
 
 
313
#else /* ISC_PLATFORM_USETHREADS */
 
314
 
 
315
        /*
 
316
         * only 1 DBI - no need to lock instance lock either
 
317
         * only 1 thread in the whole process, no possible contention.
 
318
         */
 
319
        dbi =  (dbinstance_t *) dbdata;
 
320
 
 
321
#endif /* ISC_PLATFORM_USETHREADS */
 
322
 
 
323
#if 0
 
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);
 
328
#endif
 
329
 
 
330
        /* if DBI is null, can't do anything else */
 
331
        if (dbi == NULL) {
 
332
                result = ISC_R_FAILURE;
 
333
                goto cleanup;
 
334
        }
 
335
 
 
336
        /* what type of query are we going to run? */
 
337
        switch(query) {
 
338
        case ALLNODES:
 
339
                /*
 
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!!!
 
344
                 */
 
345
                if (dbi->allnodes_q == NULL) {
 
346
                        result = ISC_R_NOTIMPLEMENTED;
 
347
                        goto cleanup;
 
348
                }
 
349
                break;
 
350
        case ALLOWXFR:
 
351
                /* same as comments as ALLNODES */
 
352
                if (dbi->allowxfr_q == NULL) {
 
353
                        result = ISC_R_NOTIMPLEMENTED;
 
354
                        goto cleanup;
 
355
                }
 
356
                break;
 
357
        case AUTHORITY:
 
358
                /* same as comments as ALLNODES */
 
359
                if (dbi->authority_q == NULL) {
 
360
                        result = ISC_R_NOTIMPLEMENTED;
 
361
                        goto cleanup;
 
362
                }
 
363
                break;
 
364
        case FINDZONE:
 
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;
 
372
                        goto cleanup;
 
373
                }
 
374
                break;
 
375
        case LOOKUP:
 
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;
 
383
                        goto cleanup;
 
384
                }
 
385
                break;
 
386
        default:
 
387
                /*
 
388
                 * this should never happen.  If it does, the code is
 
389
                 * screwed up!
 
390
                 */
 
391
                UNEXPECTED_ERROR(__FILE__, __LINE__,
 
392
                                 "Incorrect query flag passed to "
 
393
                                 "postgres_get_resultset");
 
394
                result = ISC_R_UNEXPECTED;
 
395
                goto cleanup;
 
396
        }
 
397
 
 
398
#if 0
 
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);
 
403
#endif
 
404
 
 
405
        /*
 
406
         * was a zone string passed?  If so, make it safe for use in
 
407
         * queries.
 
408
         */
 
409
        if (zone != NULL) {
 
410
                dbi->zone = postgres_escape_string(zone);
 
411
                if (dbi->zone == NULL) {
 
412
                        result = ISC_R_NOMEMORY;
 
413
                        goto cleanup;
 
414
                }
 
415
        } else {        /* no string passed, set the string pointer to NULL */
 
416
                dbi->zone = NULL;
 
417
        }
 
418
 
 
419
#if 0
 
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);
 
424
#endif
 
425
 
 
426
        /*
 
427
         * was a record string passed?  If so, make it safe for use in
 
428
         * queries.
 
429
         */
 
430
        if (record != NULL) {
 
431
                dbi->record = postgres_escape_string(record);
 
432
                if (dbi->record == NULL) {
 
433
                        result = ISC_R_NOMEMORY;
 
434
                        goto cleanup;
 
435
                }
 
436
        } else {        /* no string passed, set the string pointer to NULL */
 
437
                dbi->record = NULL;
 
438
        }
 
439
 
 
440
 
 
441
#if 0
 
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);
 
446
#endif
 
447
 
 
448
        /*
 
449
         * was a client string passed?  If so, make it safe for use in
 
450
         * queries.
 
451
         */
 
452
        if (client != NULL) {
 
453
                dbi->client = postgres_escape_string(client);
 
454
                if (dbi->client == NULL) {
 
455
                        result = ISC_R_NOMEMORY;
 
456
                        goto cleanup;
 
457
                }
 
458
        } else {        /* no string passed, set the string pointer to NULL */
 
459
                dbi->client = NULL;
 
460
        }
 
461
 
 
462
#if 0
 
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);
 
467
#endif
 
468
 
 
469
        /*
 
470
         * what type of query are we going to run?
 
471
         * this time we build the actual query to run.
 
472
         */
 
473
        switch(query) {
 
474
        case ALLNODES:
 
475
                querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
 
476
                break;
 
477
        case ALLOWXFR:
 
478
                querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
 
479
                break;
 
480
        case AUTHORITY:
 
481
                querystring = build_querystring(ns_g_mctx, dbi->authority_q);
 
482
                break;
 
483
        case FINDZONE:
 
484
                querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
 
485
                break;
 
486
        case LOOKUP:
 
487
                querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
 
488
                break;
 
489
        default:
 
490
                /*
 
491
                 * this should never happen.  If it does, the code is
 
492
                 * screwed up!
 
493
                 */
 
494
                UNEXPECTED_ERROR(__FILE__, __LINE__,
 
495
                                 "Incorrect query flag passed to "
 
496
                                 "postgres_get_resultset");
 
497
                result = ISC_R_UNEXPECTED;
 
498
                goto cleanup;
 
499
        }
 
500
 
 
501
#if 0
 
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);
 
506
#endif
 
507
 
 
508
        /* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
 
509
        if (querystring  == NULL) {
 
510
                result = ISC_R_NOMEMORY;
 
511
                goto cleanup;
 
512
        }
 
513
 
 
514
#if 0
 
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);
 
519
#endif
 
520
 
 
521
        /*
 
522
         * output the full query string during debug so we can see
 
523
         * what lame error the query has.
 
524
         */
 
525
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 
526
                      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
 
527
                      "\nQuery String: %s\n", querystring);
 
528
 
 
529
        /* attempt query up to 3 times. */
 
530
        for (j=0; j < 3; j++) {
 
531
#if 0
 
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",
 
536
                              dlz_thread_num, j);
 
537
#endif
 
538
                /* try to get result set */
 
539
                *rs = PQexec((PGconn *)dbi->dbconn, querystring );
 
540
                result = ISC_R_SUCCESS;
 
541
                /*
 
542
                 * if result set is null, reset DB connection, max 3
 
543
                 * attempts.
 
544
                 */
 
545
                for (i=0; *rs == NULL && i < 3; i++) {
 
546
#if 0
 
547
                        /* temporary logging message */
 
548
                        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 
549
                                      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
 
550
                                      "%d resetting connection",
 
551
                                      dlz_thread_num);
 
552
#endif
 
553
                        result = ISC_R_FAILURE;
 
554
                        PQreset((PGconn *) dbi->dbconn);
 
555
                        /* connection ok, break inner loop */
 
556
                        if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK)
 
557
                                break;
 
558
                }
 
559
                /* result set ok, break outter loop */
 
560
                if (PQresultStatus(*rs) == PGRES_TUPLES_OK) {
 
561
#if 0
 
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);
 
566
#endif
 
567
                        break;
 
568
                } else {
 
569
                        /* we got a result set object, but it's not right. */
 
570
#if 0
 
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);
 
575
#endif
 
576
                        PQclear(*rs);   /* get rid of it */
 
577
                        /* in case this was the last attempt */
 
578
                        result = ISC_R_FAILURE;
 
579
                }
 
580
        }
 
581
 
 
582
 cleanup:
 
583
        /* it's always good to cleanup after yourself */
 
584
 
 
585
#if 0
 
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);
 
590
#endif
 
591
 
 
592
        /* if we couldn't even allocate DBI, just return NULL */
 
593
        if (dbi == NULL)
 
594
                return ISC_R_FAILURE;
 
595
 
 
596
        /* free dbi->zone string */
 
597
        if (dbi->zone != NULL)
 
598
                isc_mem_free(ns_g_mctx, dbi->zone);
 
599
 
 
600
        /* free dbi->record string */
 
601
        if (dbi->record != NULL)
 
602
                isc_mem_free(ns_g_mctx, dbi->record);
 
603
 
 
604
        /* free dbi->client string */
 
605
        if (dbi->client != NULL)
 
606
                isc_mem_free(ns_g_mctx, dbi->client);
 
607
 
 
608
#ifdef ISC_PLATFORM_USETHREADS
 
609
 
 
610
#if 0
 
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);
 
615
#endif
 
616
 
 
617
        /* release the lock so another thread can use this dbi */
 
618
        isc_mutex_unlock(&dbi->instance_lock);
 
619
 
 
620
#endif /* ISC_PLATFORM_USETHREADS */
 
621
 
 
622
        /* release query string */
 
623
        if (querystring  != NULL)
 
624
                isc_mem_free(ns_g_mctx, querystring );
 
625
 
 
626
#if 0
 
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);
 
631
#endif
 
632
 
 
633
        /* return result */
 
634
        return result;
 
635
}
 
636
 
 
637
/*%
 
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.
 
641
 */
 
642
 
 
643
static isc_result_t
 
644
postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs)
 
645
{
 
646
        isc_result_t result;
 
647
        unsigned int i;
 
648
        unsigned int rows;
 
649
        unsigned int fields;
 
650
        unsigned int j;
 
651
        unsigned int len;
 
652
        char *tmpString;
 
653
        char *endp;
 
654
        int ttl;
 
655
 
 
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++) {
 
659
                switch(fields) {
 
660
                case 1:
 
661
                        /*
 
662
                         * one column in rs, it's the data field.  use
 
663
                         * default type of A record, and default TTL
 
664
                         * of 86400
 
665
                         */
 
666
                        result = dns_sdlz_putrr(lookup, "a", 86400,
 
667
                                                PQgetvalue(rs, i, 0));
 
668
                        break;
 
669
                case 2:
 
670
                        /* two columns, data field, and data type.
 
671
                         * use default TTL of 86400.
 
672
                         */
 
673
                        result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0),
 
674
                                                86400, PQgetvalue(rs, i, 1));
 
675
                        break;
 
676
                case 3:
 
677
                        /* three columns, all data no defaults.
 
678
                         * convert text to int, make sure it worked
 
679
                         * right.
 
680
                         */
 
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");
 
688
                        }
 
689
                        result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
 
690
                                                ttl, PQgetvalue(rs, i, 2));
 
691
                        break;
 
692
                default:
 
693
                        /*
 
694
                         * more than 3 fields, concatenate the last
 
695
                         * ones together.  figure out how long to make
 
696
                         * string
 
697
                         */
 
698
                        for (j=2, len=0; j < fields; j++) {
 
699
                                len += strlen(PQgetvalue(rs, i, j)) + 1;
 
700
                        }
 
701
                        /*
 
702
                         * allocate string memory, allow for NULL to
 
703
                         * term string
 
704
                         */
 
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 "
 
713
                                              "temporary string");
 
714
                                PQclear(rs);
 
715
                                return (ISC_R_FAILURE); /* Yeah, I'd say! */
 
716
                        }
 
717
                        /* copy field to tmpString */
 
718
                        strcpy(tmpString, PQgetvalue(rs, i, 2));
 
719
                        /*
 
720
                         * concat the rest of fields together, space
 
721
                         * between each one.
 
722
                         */
 
723
                        for (j=3; j < fields; j++) {
 
724
                                strcat(tmpString, " ");
 
725
                                strcat(tmpString, PQgetvalue(rs, i, j));
 
726
                        }
 
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 "
 
734
                                              "a postive number");
 
735
                        }
 
736
                        /* ok, now tell Bind about it. */
 
737
                        result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
 
738
                                                ttl, tmpString);
 
739
                        /* done, get rid of this thing. */
 
740
                        isc_mem_free(ns_g_mctx, tmpString);
 
741
                }
 
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 */
 
745
                        PQclear(rs);
 
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);
 
752
                }
 
753
        }
 
754
 
 
755
        /* free result set memory */
 
756
        PQclear(rs);
 
757
 
 
758
        /* if we did return results, we are successful */
 
759
        if (rows > 0)
 
760
                return (ISC_R_SUCCESS);
 
761
 
 
762
        /* empty result set, no data found */
 
763
        return (ISC_R_NOTFOUND);
 
764
}
 
765
 
 
766
/*
 
767
 * SDLZ interface methods
 
768
 */
 
769
 
 
770
/*% determine if the zone is supported by (in) the database */
 
771
 
 
772
static isc_result_t
 
773
postgres_findzone(void *driverarg, void *dbdata, const char *name)
 
774
{
 
775
        isc_result_t result;
 
776
        PGresult *rs = NULL;
 
777
        unsigned int rows;
 
778
        UNUSED(driverarg);
 
779
 
 
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) {
 
785
                if (rs != NULL)
 
786
                        PQclear(rs);
 
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);
 
792
        }
 
793
        /* count how many rows in result set */
 
794
        rows = PQntuples(rs);
 
795
        /* get rid of result set, we are done with it. */
 
796
        PQclear(rs);
 
797
 
 
798
        /* if we returned any rows, zone is supported. */
 
799
        if (rows > 0)
 
800
                return (ISC_R_SUCCESS);
 
801
 
 
802
        /* no rows returned, zone is not supported. */
 
803
        return (ISC_R_NOTFOUND);
 
804
}
 
805
 
 
806
/*% Determine if the client is allowed to perform a zone transfer */
 
807
static isc_result_t
 
808
postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name,
 
809
                      const char *client)
 
810
{
 
811
        isc_result_t result;
 
812
        PGresult *rs = NULL;
 
813
        unsigned int rows;
 
814
        UNUSED(driverarg);
 
815
 
 
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);
 
820
 
 
821
        /*
 
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.
 
826
         *
 
827
         * Run our query, and get a result set from the database.
 
828
         */
 
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)
 
833
                return result;
 
834
        /* if we didn't get a result set, log an err msg. */
 
835
        if (result != ISC_R_SUCCESS) {
 
836
                if (rs != NULL)
 
837
                        PQclear(rs);
 
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);
 
843
        }
 
844
        /* count how many rows in result set */
 
845
        rows = PQntuples(rs);
 
846
        /* get rid of result set, we are done with it. */
 
847
        PQclear(rs);
 
848
 
 
849
        /* if we returned any rows, zone xfr is allowed. */
 
850
        if (rows > 0)
 
851
                return (ISC_R_SUCCESS);
 
852
 
 
853
        /* no rows returned, zone xfr not allowed */
 
854
        return (ISC_R_NOPERM);
 
855
}
 
856
 
 
857
/*%
 
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
 
860
 * query.
 
861
 */
 
862
static isc_result_t
 
863
postgres_allnodes(const char *zone, void *driverarg, void *dbdata,
 
864
                  dns_sdlzallnodes_t *allnodes)
 
865
{
 
866
        isc_result_t result;
 
867
        PGresult *rs = NULL;
 
868
        unsigned int i;
 
869
        unsigned int rows;
 
870
        unsigned int fields;
 
871
        unsigned int j;
 
872
        unsigned int len;
 
873
        char *tmpString;
 
874
        char *endp;
 
875
        int ttl;
 
876
 
 
877
        UNUSED(driverarg);
 
878
 
 
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)
 
884
                return result;
 
885
        /* if we didn't get a result set, log an err msg. */
 
886
        if (result != ISC_R_SUCCESS) {
 
887
                if (rs != NULL)
 
888
                        PQclear(rs);
 
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);
 
894
        }
 
895
 
 
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");
 
904
                }
 
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 "
 
911
                                      "a postive number");
 
912
                }
 
913
                if (fields == 4) {
 
914
                        /* tell Bind about it. */
 
915
                        result = dns_sdlz_putnamedrr(allnodes,
 
916
                                                     PQgetvalue(rs, i, 2),
 
917
                                                     PQgetvalue(rs, i, 1),
 
918
                                                     ttl,
 
919
                                                     PQgetvalue(rs, i, 3));
 
920
                } else {
 
921
                        /*
 
922
                         * more than 4 fields, concatonat the last
 
923
                         * ones together.  figure out how long to make
 
924
                         * string
 
925
                         */
 
926
                        for (j=3, len=0; j < fields; j++) {
 
927
                                len += strlen(PQgetvalue(rs, i, j)) + 1;
 
928
                        }
 
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 "
 
937
                                              "temporary string");
 
938
                                PQclear(rs);
 
939
                                return (ISC_R_FAILURE);
 
940
                        }
 
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));
 
947
                        }
 
948
                        /* tell Bind about it. */
 
949
                        result = dns_sdlz_putnamedrr(allnodes,
 
950
                                                     PQgetvalue(rs, i, 2),
 
951
                                                     PQgetvalue(rs, i, 1),
 
952
                                                     ttl, tmpString);
 
953
                        isc_mem_free(ns_g_mctx, tmpString);
 
954
                }
 
955
                /* if we weren't successful, log err msg */
 
956
                if (result != ISC_R_SUCCESS) {
 
957
                        PQclear(rs);
 
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);
 
964
                }
 
965
        }
 
966
 
 
967
        /* free result set memory */
 
968
        PQclear(rs);
 
969
 
 
970
        /* if we did return results, we are successful */
 
971
        if (rows > 0)
 
972
                return (ISC_R_SUCCESS);
 
973
 
 
974
        /* empty result set, no data found */
 
975
        return (ISC_R_NOTFOUND);
 
976
}
 
977
 
 
978
/*%
 
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.
 
981
 */
 
982
 
 
983
static isc_result_t
 
984
postgres_authority(const char *zone, void *driverarg, void *dbdata,
 
985
                   dns_sdlzlookup_t *lookup)
 
986
{
 
987
        isc_result_t result;
 
988
        PGresult *rs = NULL;
 
989
 
 
990
        UNUSED(driverarg);
 
991
 
 
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)
 
997
                return result;
 
998
        /* if we didn't get a result set, log an err msg. */
 
999
        if (result != ISC_R_SUCCESS) {
 
1000
                if (rs != NULL)
 
1001
                        PQclear(rs);
 
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);
 
1007
        }
 
1008
        /*
 
1009
         * lookup and authority result sets are processed in the same
 
1010
         * manner postgres_process_rs does the job for both
 
1011
         * functions.
 
1012
         */
 
1013
        return postgres_process_rs(lookup, rs);
 
1014
}
 
1015
 
 
1016
/*% if zone is supported, lookup up a (or multiple) record(s) in it */
 
1017
static isc_result_t
 
1018
postgres_lookup(const char *zone, const char *name, void *driverarg,
 
1019
                void *dbdata, dns_sdlzlookup_t *lookup)
 
1020
{
 
1021
        isc_result_t result;
 
1022
        PGresult *rs = NULL;
 
1023
 
 
1024
        UNUSED(driverarg);
 
1025
 
 
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) {
 
1030
                if (rs != NULL)
 
1031
                        PQclear(rs);
 
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);
 
1037
        }
 
1038
        /*
 
1039
         * lookup and authority result sets are processed in the same
 
1040
         * manner postgres_process_rs does the job for both functions.
 
1041
         */
 
1042
        return postgres_process_rs(lookup, rs);
 
1043
}
 
1044
 
 
1045
/*%
 
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.
 
1050
 */
 
1051
static isc_result_t
 
1052
postgres_create(const char *dlzname, unsigned int argc, char *argv[],
 
1053
                void *driverarg, void **dbdata)
 
1054
{
 
1055
        isc_result_t result;
 
1056
        dbinstance_t *dbi = NULL;
 
1057
        unsigned int j;
 
1058
 
 
1059
#ifdef ISC_PLATFORM_USETHREADS
 
1060
        /* if multi-threaded, we need a few extra variables. */
 
1061
        int dbcount;
 
1062
        db_list_t *dblist = NULL;
 
1063
        int i;
 
1064
        char *endp;
 
1065
 
 
1066
#endif /* ISC_PLATFORM_USETHREADS */
 
1067
 
 
1068
        UNUSED(driverarg);
 
1069
        UNUSED(dlzname);
 
1070
 
 
1071
/* seed random # generator */
 
1072
        srand( (unsigned)time( NULL ) );
 
1073
 
 
1074
 
 
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 */
 
1086
 
 
1087
        /* verify we have at least 5 arg's passed to the driver */
 
1088
        if (argc < 5) {
 
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);
 
1094
        }
 
1095
 
 
1096
        /* no more than 8 arg's should be passed to the driver */
 
1097
        if (argc > 8) {
 
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);
 
1103
        }
 
1104
 
 
1105
        /* multithreaded build can have multiple DB connections */
 
1106
#ifdef ISC_PLATFORM_USETHREADS
 
1107
 
 
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);
 
1116
        }
 
1117
 
 
1118
        /* allocate memory for database connection list */
 
1119
        dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
 
1120
        if (dblist == NULL)
 
1121
                return (ISC_R_NOMEMORY);
 
1122
 
 
1123
        /* initialize DB connection list */
 
1124
        ISC_LIST_INIT(*dblist);
 
1125
 
 
1126
        /*
 
1127
         * create the appropriate number of database instances (DBI)
 
1128
         * append each new DBI to the end of the list
 
1129
         */
 
1130
        for (i=0; i < dbcount; i++) {
 
1131
 
 
1132
#endif /* ISC_PLATFORM_USETHREADS */
 
1133
 
 
1134
                /* how many queries were passed in from config file? */
 
1135
                switch(argc) {
 
1136
                case 5:
 
1137
                        result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
 
1138
                                                     NULL, argv[3], argv[4],
 
1139
                                                     NULL, &dbi);
 
1140
                        break;
 
1141
                case 6:
 
1142
                        result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
 
1143
                                                     argv[5], argv[3], argv[4],
 
1144
                                                     NULL, &dbi);
 
1145
                        break;
 
1146
                case 7:
 
1147
                        result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
 
1148
                                                     argv[5], argv[3], argv[4],
 
1149
                                                     NULL, &dbi);
 
1150
                        break;
 
1151
                case 8:
 
1152
                        result = build_sqldbinstance(ns_g_mctx, argv[6],
 
1153
                                                     argv[7], argv[5], argv[3],
 
1154
                                                     argv[4], NULL, &dbi);
 
1155
                        break;
 
1156
                default:
 
1157
                        /* not really needed, should shut up compiler. */
 
1158
                        result = ISC_R_FAILURE;
 
1159
                }
 
1160
 
 
1161
 
 
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.");
 
1172
                        goto cleanup;
 
1173
                }
 
1174
 
 
1175
#ifdef ISC_PLATFORM_USETHREADS
 
1176
 
 
1177
                /* when multithreaded, build a list of DBI's */
 
1178
                ISC_LINK_INIT(dbi, link);
 
1179
                ISC_LIST_APPEND(*dblist, dbi, link);
 
1180
 
 
1181
#endif
 
1182
 
 
1183
                /* create and set db connection */
 
1184
                dbi->dbconn = PQconnectdb(argv[2]);
 
1185
                /*
 
1186
                 * if db connection cannot be created, log err msg and
 
1187
                 * cleanup.
 
1188
                 */
 
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");
 
1194
                        goto cleanup;
 
1195
                }
 
1196
 
 
1197
                /* if we cannot connect the first time, try 3 more times. */
 
1198
                for (j = 0;
 
1199
                     PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK &&
 
1200
                             j < 3;
 
1201
                     j++)
 
1202
                        PQreset((PGconn *) dbi->dbconn);
 
1203
 
 
1204
 
 
1205
#ifdef ISC_PLATFORM_USETHREADS
 
1206
 
 
1207
                /*
 
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
 
1211
                 * allows 9
 
1212
                 */
 
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 "
 
1218
                                      "after 4 attempts",
 
1219
                                      i + 1);
 
1220
                        goto cleanup;
 
1221
                }
 
1222
 
 
1223
                /* set DBI = null for next loop through. */
 
1224
                dbi = NULL;
 
1225
        }       /* end for loop */
 
1226
 
 
1227
                /* set dbdata to the list we created. */
 
1228
        *dbdata = dblist;
 
1229
 
 
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");
 
1237
                goto cleanup;
 
1238
        }
 
1239
 
 
1240
        /*
 
1241
         * single threaded build can only use 1 db connection, return
 
1242
         * it via dbdata
 
1243
         */
 
1244
        *dbdata = dbi;
 
1245
 
 
1246
#endif /* ISC_PLATFORM_USETHREADS */
 
1247
 
 
1248
        /* hey, we got through all of that ok, return success. */
 
1249
        return(ISC_R_SUCCESS);
 
1250
 
 
1251
 cleanup:
 
1252
 
 
1253
#ifdef ISC_PLATFORM_USETHREADS
 
1254
        /*
 
1255
         * if multithreaded, we could fail because only 1 connection
 
1256
         * couldn't be made.  We should cleanup the other successful
 
1257
         * connections properly.
 
1258
         */
 
1259
        postgres_destroy_dblist(dblist);
 
1260
 
 
1261
#else /* ISC_PLATFORM_USETHREADS */
 
1262
        if (dbi != NULL)
 
1263
                destroy_sqldbinstance(dbi);
 
1264
 
 
1265
#endif /* ISC_PLATFORM_USETHREADS */
 
1266
        return(ISC_R_FAILURE);
 
1267
}
 
1268
 
 
1269
/*%
 
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.
 
1274
 */
 
1275
static void
 
1276
postgres_destroy(void *driverarg, void *dbdata)
 
1277
{
 
1278
 
 
1279
#ifdef ISC_PLATFORM_USETHREADS
 
1280
 
 
1281
        UNUSED(driverarg);
 
1282
        /* cleanup the list of DBI's */
 
1283
        postgres_destroy_dblist((db_list_t *) dbdata);
 
1284
 
 
1285
#else /* ISC_PLATFORM_USETHREADS */
 
1286
 
 
1287
        dbinstance_t *dbi;
 
1288
 
 
1289
        UNUSED(driverarg);
 
1290
 
 
1291
        dbi = (dbinstance_t *) dbdata;
 
1292
 
 
1293
        /* release DB connection */
 
1294
        if (dbi->dbconn != NULL)
 
1295
                PQfinish((PGconn *) dbi->dbconn);
 
1296
 
 
1297
        /* destroy single DB instance */
 
1298
        destroy_sqldbinstance(dbi);
 
1299
 
 
1300
#endif /* ISC_PLATFORM_USETHREADS */
 
1301
}
 
1302
 
 
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 = {
 
1307
        postgres_create,
 
1308
        postgres_destroy,
 
1309
        postgres_findzone,
 
1310
        postgres_lookup,
 
1311
        postgres_authority,
 
1312
        postgres_allnodes,
 
1313
        postgres_allowzonexfr
 
1314
};
 
1315
 
 
1316
/*%
 
1317
 * Wrapper around dns_sdlzregister().
 
1318
 */
 
1319
isc_result_t
 
1320
dlz_postgres_init(void) {
 
1321
        isc_result_t result;
 
1322
 
 
1323
        /*
 
1324
         * Write debugging message to log
 
1325
         */
 
1326
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 
1327
                      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
 
1328
                      "Registering DLZ postgres driver.");
 
1329
 
 
1330
        /*
 
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
 
1335
         * threadsafe.
 
1336
         */
 
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;
 
1348
        }
 
1349
 
 
1350
 
 
1351
        return result;
 
1352
}
 
1353
 
 
1354
/*%
 
1355
 * Wrapper around dns_sdlzunregister().
 
1356
 */
 
1357
void
 
1358
dlz_postgres_clear(void) {
 
1359
 
 
1360
        /*
 
1361
         * Write debugging message to log
 
1362
         */
 
1363
        isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 
1364
                      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
 
1365
                      "Unregistering DLZ postgres driver.");
 
1366
 
 
1367
        /* unregister the driver. */
 
1368
        if (dlz_postgres != NULL)
 
1369
                dns_sdlzunregister(&dlz_postgres);
 
1370
}
 
1371
 
 
1372
#endif