[BACK]Return to dlz_postgres_driver.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / external / mpl / bind / dist / contrib / dlz / drivers

Annotation of src/external/mpl/bind/dist/contrib/dlz/drivers/dlz_postgres_driver.c, Revision 1.1.1.3

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

CVSweb <webmaster@jp.NetBSD.org>