4 * Copyright (C) 2004, 2007 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.15 2007/06/19 23:47:07 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.
117 pgsqldb_lookup(const char *zone
, const char *name
, void *dbdata
,
118 dns_sdblookup_t
*lookup
)
121 struct dbinfo
*dbi
= dbdata
;
129 canonname
= isc_mem_get(ns_g_mctx
, strlen(name
) * 2 + 1);
130 if (canonname
== NULL
)
131 return (ISC_R_NOMEMORY
);
132 quotestring(name
, canonname
);
133 snprintf(str
, sizeof(str
),
134 "SELECT TTL,RDTYPE,RDATA FROM \"%s\" WHERE "
135 "lower(NAME) = lower('%s')", dbi
->table
, canonname
);
136 isc_mem_put(ns_g_mctx
, canonname
, strlen(name
) * 2 + 1);
138 result
= maybe_reconnect(dbi
);
139 if (result
!= ISC_R_SUCCESS
)
142 res
= PQexec(dbi
->conn
, str
);
143 if (!res
|| PQresultStatus(res
) != PGRES_TUPLES_OK
) {
145 return (ISC_R_FAILURE
);
147 if (PQntuples(res
) == 0) {
149 return (ISC_R_NOTFOUND
);
152 for (i
= 0; i
< PQntuples(res
); i
++) {
153 char *ttlstr
= PQgetvalue(res
, i
, 0);
154 char *type
= PQgetvalue(res
, i
, 1);
155 char *data
= PQgetvalue(res
, i
, 2);
158 ttl
= strtol(ttlstr
, &endp
, 10);
161 return (DNS_R_BADTTL
);
163 result
= dns_sdb_putrr(lookup
, type
, ttl
, data
);
164 if (result
!= ISC_R_SUCCESS
) {
166 return (ISC_R_FAILURE
);
171 return (ISC_R_SUCCESS
);
175 * Issue an SQL query to return all nodes in the database and fill the
176 * allnodes structure.
179 pgsqldb_allnodes(const char *zone
, void *dbdata
, dns_sdballnodes_t
*allnodes
) {
180 struct dbinfo
*dbi
= dbdata
;
188 snprintf(str
, sizeof(str
),
189 "SELECT TTL,NAME,RDTYPE,RDATA FROM \"%s\" ORDER BY NAME",
192 result
= maybe_reconnect(dbi
);
193 if (result
!= ISC_R_SUCCESS
)
196 res
= PQexec(dbi
->conn
, str
);
197 if (!res
|| PQresultStatus(res
) != PGRES_TUPLES_OK
) {
199 return (ISC_R_FAILURE
);
201 if (PQntuples(res
) == 0) {
203 return (ISC_R_NOTFOUND
);
206 for (i
= 0; i
< PQntuples(res
); i
++) {
207 char *ttlstr
= PQgetvalue(res
, i
, 0);
208 char *name
= PQgetvalue(res
, i
, 1);
209 char *type
= PQgetvalue(res
, i
, 2);
210 char *data
= PQgetvalue(res
, i
, 3);
213 ttl
= strtol(ttlstr
, &endp
, 10);
216 return (DNS_R_BADTTL
);
218 result
= dns_sdb_putnamedrr(allnodes
, name
, type
, ttl
, data
);
219 if (result
!= ISC_R_SUCCESS
) {
221 return (ISC_R_FAILURE
);
226 return (ISC_R_SUCCESS
);
230 * Create a connection to the database and save any necessary information
233 * argv[0] is the name of the database
234 * argv[1] is the name of the table
235 * argv[2] (if present) is the name of the host to connect to
236 * argv[3] (if present) is the name of the user to connect as
237 * argv[4] (if present) is the name of the password to connect with
240 pgsqldb_create(const char *zone
, int argc
, char **argv
,
241 void *driverdata
, void **dbdata
)
250 return (ISC_R_FAILURE
);
252 dbi
= isc_mem_get(ns_g_mctx
, sizeof(struct dbinfo
));
254 return (ISC_R_NOMEMORY
);
256 dbi
->database
= NULL
;
262 #define STRDUP_OR_FAIL(target, source) \
264 target = isc_mem_strdup(ns_g_mctx, source); \
265 if (target == NULL) { \
266 result = ISC_R_NOMEMORY; \
271 STRDUP_OR_FAIL(dbi
->database
, argv
[0]);
272 STRDUP_OR_FAIL(dbi
->table
, argv
[1]);
274 STRDUP_OR_FAIL(dbi
->host
, argv
[2]);
276 STRDUP_OR_FAIL(dbi
->user
, argv
[3]);
278 STRDUP_OR_FAIL(dbi
->passwd
, argv
[4]);
280 result
= db_connect(dbi
);
281 if (result
!= ISC_R_SUCCESS
)
285 return (ISC_R_SUCCESS
);
288 pgsqldb_destroy(zone
, driverdata
, (void **)&dbi
);
293 * Close the connection to the database.
296 pgsqldb_destroy(const char *zone
, void *driverdata
, void **dbdata
) {
297 struct dbinfo
*dbi
= *dbdata
;
302 if (dbi
->conn
!= NULL
)
304 if (dbi
->database
!= NULL
)
305 isc_mem_free(ns_g_mctx
, dbi
->database
);
306 if (dbi
->table
!= NULL
)
307 isc_mem_free(ns_g_mctx
, dbi
->table
);
308 if (dbi
->host
!= NULL
)
309 isc_mem_free(ns_g_mctx
, dbi
->host
);
310 if (dbi
->user
!= NULL
)
311 isc_mem_free(ns_g_mctx
, dbi
->user
);
312 if (dbi
->passwd
!= NULL
)
313 isc_mem_free(ns_g_mctx
, dbi
->passwd
);
314 if (dbi
->database
!= NULL
)
315 isc_mem_free(ns_g_mctx
, dbi
->database
);
316 isc_mem_put(ns_g_mctx
, dbi
, sizeof(struct dbinfo
));
320 * Since the SQL database corresponds to a zone, the authority data should
321 * be returned by the lookup() function. Therefore the authority() function
324 static dns_sdbmethods_t pgsqldb_methods
= {
326 NULL
, /* authority */
333 * Wrapper around dns_sdb_register().
339 return (dns_sdb_register("pgsql", &pgsqldb_methods
, NULL
, flags
,
340 ns_g_mctx
, &pgsqldb
));
344 * Wrapper around dns_sdb_unregister().
347 pgsqldb_clear(void) {
349 dns_sdb_unregister(&pgsqldb
);