1 /* $NetBSD: pgsqldb.c,v 1.4 2014/12/10 04:37:57 christos Exp $ */
4 * Copyright (C) 2004, 2007, 2011, 2014 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000, 2001 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: pgsqldb.c,v 1.17 2011/10/11 23:46:45 tbox Exp */
28 #include <pgsql/libpq-fe.h>
31 #include <isc/print.h>
32 #include <isc/result.h>
36 #include <dns/result.h>
38 #include <named/globals.h>
43 * A simple database driver that interfaces to a PostgreSQL database. This
44 * is not complete, and not designed for general use. It opens one
45 * connection to the database per zone, which is inefficient. It also may
46 * not handle quoting correctly.
48 * The table must contain the fields "name", "rdtype", and "rdata", and
49 * is expected to contain a properly constructed zone. The program "zonetodb"
50 * creates such a table.
53 static dns_sdbimplementation_t
*pgsqldb
= NULL
;
65 pgsqldb_destroy(const char *zone
, void *driverdata
, void **dbdata
);
68 * Canonicalize a string before writing it to the database.
69 * "dest" must be an array of at least size 2*strlen(source) + 1.
72 quotestring(const char *source
, char *dest
) {
73 while (*source
!= 0) {
76 /* SQL doesn't treat \ as special, but PostgreSQL does */
77 else if (*source
== '\\')
85 * Connect to the database.
88 db_connect(struct dbinfo
*dbi
) {
89 dbi
->conn
= PQsetdbLogin(dbi
->host
, NULL
, NULL
, NULL
, dbi
->database
,
90 dbi
->user
, dbi
->passwd
);
92 if (PQstatus(dbi
->conn
) == CONNECTION_OK
)
93 return (ISC_R_SUCCESS
);
95 return (ISC_R_FAILURE
);
99 * Check to see if the connection is still valid. If not, attempt to
103 maybe_reconnect(struct dbinfo
*dbi
) {
104 if (PQstatus(dbi
->conn
) == CONNECTION_OK
)
105 return (ISC_R_SUCCESS
);
107 return (db_connect(dbi
));
111 * This database operates on absolute names.
113 * Queries are converted into SQL queries and issued synchronously. Errors
114 * are handled really badly.
116 #ifdef DNS_CLIENTINFO_VERSION
118 pgsqldb_lookup(const char *zone
, const char *name
, void *dbdata
,
119 dns_sdblookup_t
*lookup
, dns_clientinfomethods_t
*methods
,
120 dns_clientinfo_t
*clientinfo
)
123 pgsqldb_lookup(const char *zone
, const char *name
, void *dbdata
,
124 dns_sdblookup_t
*lookup
)
125 #endif /* DNS_CLIENTINFO_VERSION */
128 struct dbinfo
*dbi
= dbdata
;
135 #ifdef DNS_CLIENTINFO_VERSION
138 #endif /* DNS_CLIENTINFO_VERSION */
140 canonname
= isc_mem_get(ns_g_mctx
, strlen(name
) * 2 + 1);
141 if (canonname
== NULL
)
142 return (ISC_R_NOMEMORY
);
143 quotestring(name
, canonname
);
144 snprintf(str
, sizeof(str
),
145 "SELECT TTL,RDTYPE,RDATA FROM \"%s\" WHERE "
146 "lower(NAME) = lower('%s')", dbi
->table
, canonname
);
147 isc_mem_put(ns_g_mctx
, canonname
, strlen(name
) * 2 + 1);
149 result
= maybe_reconnect(dbi
);
150 if (result
!= ISC_R_SUCCESS
)
153 res
= PQexec(dbi
->conn
, str
);
154 if (!res
|| PQresultStatus(res
) != PGRES_TUPLES_OK
) {
156 return (ISC_R_FAILURE
);
158 if (PQntuples(res
) == 0) {
160 return (ISC_R_NOTFOUND
);
163 for (i
= 0; i
< PQntuples(res
); i
++) {
164 char *ttlstr
= PQgetvalue(res
, i
, 0);
165 char *type
= PQgetvalue(res
, i
, 1);
166 char *data
= PQgetvalue(res
, i
, 2);
169 ttl
= strtol(ttlstr
, &endp
, 10);
172 return (DNS_R_BADTTL
);
174 result
= dns_sdb_putrr(lookup
, type
, ttl
, data
);
175 if (result
!= ISC_R_SUCCESS
) {
177 return (ISC_R_FAILURE
);
182 return (ISC_R_SUCCESS
);
186 * Issue an SQL query to return all nodes in the database and fill the
187 * allnodes structure.
190 pgsqldb_allnodes(const char *zone
, void *dbdata
, dns_sdballnodes_t
*allnodes
) {
191 struct dbinfo
*dbi
= dbdata
;
199 snprintf(str
, sizeof(str
),
200 "SELECT TTL,NAME,RDTYPE,RDATA FROM \"%s\" ORDER BY NAME",
203 result
= maybe_reconnect(dbi
);
204 if (result
!= ISC_R_SUCCESS
)
207 res
= PQexec(dbi
->conn
, str
);
208 if (!res
|| PQresultStatus(res
) != PGRES_TUPLES_OK
) {
210 return (ISC_R_FAILURE
);
212 if (PQntuples(res
) == 0) {
214 return (ISC_R_NOTFOUND
);
217 for (i
= 0; i
< PQntuples(res
); i
++) {
218 char *ttlstr
= PQgetvalue(res
, i
, 0);
219 char *name
= PQgetvalue(res
, i
, 1);
220 char *type
= PQgetvalue(res
, i
, 2);
221 char *data
= PQgetvalue(res
, i
, 3);
224 ttl
= strtol(ttlstr
, &endp
, 10);
227 return (DNS_R_BADTTL
);
229 result
= dns_sdb_putnamedrr(allnodes
, name
, type
, ttl
, data
);
230 if (result
!= ISC_R_SUCCESS
) {
232 return (ISC_R_FAILURE
);
237 return (ISC_R_SUCCESS
);
241 * Create a connection to the database and save any necessary information
244 * argv[0] is the name of the database
245 * argv[1] is the name of the table
246 * argv[2] (if present) is the name of the host to connect to
247 * argv[3] (if present) is the name of the user to connect as
248 * argv[4] (if present) is the name of the password to connect with
251 pgsqldb_create(const char *zone
, int argc
, char **argv
,
252 void *driverdata
, void **dbdata
)
261 return (ISC_R_FAILURE
);
263 dbi
= isc_mem_get(ns_g_mctx
, sizeof(struct dbinfo
));
265 return (ISC_R_NOMEMORY
);
267 dbi
->database
= NULL
;
273 #define STRDUP_OR_FAIL(target, source) \
275 target = isc_mem_strdup(ns_g_mctx, source); \
276 if (target == NULL) { \
277 result = ISC_R_NOMEMORY; \
280 } while (/*CONSTCOND*/0);
282 STRDUP_OR_FAIL(dbi
->database
, argv
[0]);
283 STRDUP_OR_FAIL(dbi
->table
, argv
[1]);
285 STRDUP_OR_FAIL(dbi
->host
, argv
[2]);
287 STRDUP_OR_FAIL(dbi
->user
, argv
[3]);
289 STRDUP_OR_FAIL(dbi
->passwd
, argv
[4]);
291 result
= db_connect(dbi
);
292 if (result
!= ISC_R_SUCCESS
)
296 return (ISC_R_SUCCESS
);
299 pgsqldb_destroy(zone
, driverdata
, (void **)&dbi
);
304 * Close the connection to the database.
307 pgsqldb_destroy(const char *zone
, void *driverdata
, void **dbdata
) {
308 struct dbinfo
*dbi
= *dbdata
;
313 if (dbi
->conn
!= NULL
)
315 if (dbi
->database
!= NULL
)
316 isc_mem_free(ns_g_mctx
, dbi
->database
);
317 if (dbi
->table
!= NULL
)
318 isc_mem_free(ns_g_mctx
, dbi
->table
);
319 if (dbi
->host
!= NULL
)
320 isc_mem_free(ns_g_mctx
, dbi
->host
);
321 if (dbi
->user
!= NULL
)
322 isc_mem_free(ns_g_mctx
, dbi
->user
);
323 if (dbi
->passwd
!= NULL
)
324 isc_mem_free(ns_g_mctx
, dbi
->passwd
);
325 if (dbi
->database
!= NULL
)
326 isc_mem_free(ns_g_mctx
, dbi
->database
);
327 isc_mem_put(ns_g_mctx
, dbi
, sizeof(struct dbinfo
));
331 * Since the SQL database corresponds to a zone, the authority data should
332 * be returned by the lookup() function. Therefore the authority() function
335 static dns_sdbmethods_t pgsqldb_methods
= {
337 NULL
, /* authority */
345 * Wrapper around dns_sdb_register().
351 return (dns_sdb_register("pgsql", &pgsqldb_methods
, NULL
, flags
,
352 ns_g_mctx
, &pgsqldb
));
356 * Wrapper around dns_sdb_unregister().
359 pgsqldb_clear(void) {
361 dns_sdb_unregister(&pgsqldb
);