1/*
2 * Copyright (C) 2004, 2005, 2007-2009  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: zonetodb.c,v 1.23 2009/09/02 23:48:01 tbox Exp $ */
19
20#include <stdlib.h>
21#include <string.h>
22
23#include <isc/buffer.h>
24#include <isc/entropy.h>
25#include <isc/hash.h>
26#include <isc/mem.h>
27#include <isc/print.h>
28#include <isc/result.h>
29
30#include <dns/db.h>
31#include <dns/dbiterator.h>
32#include <dns/fixedname.h>
33#include <dns/name.h>
34#include <dns/rdata.h>
35#include <dns/rdataset.h>
36#include <dns/rdatasetiter.h>
37#include <dns/rdatatype.h>
38#include <dns/result.h>
39
40#include <pgsql/libpq-fe.h>
41
42/*
43 * Generate a PostgreSQL table from a zone.
44 *
45 * This is compiled this with something like the following (assuming bind9 has
46 * been installed):
47 *
48 * gcc -g `isc-config.sh --cflags isc dns` -c zonetodb.c
49 * gcc -g -o zonetodb zonetodb.o `isc-config.sh --libs isc dns` -lpq
50 */
51
52PGconn *conn = NULL;
53char *dbname, *dbtable;
54char str[10240];
55
56void
57closeandexit(int status) {
58	if (conn != NULL)
59		PQfinish(conn);
60	exit(status);
61}
62
63void
64check_result(isc_result_t result, const char *message) {
65	if (result != ISC_R_SUCCESS) {
66		fprintf(stderr, "%s: %s\n", message,
67			isc_result_totext(result));
68		closeandexit(1);
69	}
70}
71
72/*
73 * Canonicalize a string before writing it to the database.
74 * "dest" must be an array of at least size 2*strlen(source) + 1.
75 */
76static void
77quotestring(const char *source, char *dest) {
78	while (*source != 0) {
79		if (*source == '\'')
80			*dest++ = '\'';
81		else if (*source == '\\')
82			*dest++ = '\\';
83		*dest++ = *source++;
84	}
85	*dest++ = 0;
86}
87
88void
89addrdata(dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata) {
90	unsigned char namearray[DNS_NAME_MAXTEXT + 1];
91	unsigned char canonnamearray[2 * DNS_NAME_MAXTEXT + 1];
92	unsigned char typearray[20];
93	unsigned char canontypearray[40];
94	unsigned char dataarray[2048];
95	unsigned char canondataarray[4096];
96	isc_buffer_t b;
97	isc_result_t result;
98	PGresult *res;
99
100	isc_buffer_init(&b, namearray, sizeof(namearray) - 1);
101	result = dns_name_totext(name, ISC_TRUE, &b);
102	check_result(result, "dns_name_totext");
103	namearray[isc_buffer_usedlength(&b)] = 0;
104	quotestring(namearray, canonnamearray);
105
106	isc_buffer_init(&b, typearray, sizeof(typearray) - 1);
107	result = dns_rdatatype_totext(rdata->type, &b);
108	check_result(result, "dns_rdatatype_totext");
109	typearray[isc_buffer_usedlength(&b)] = 0;
110	quotestring(typearray, canontypearray);
111
112	isc_buffer_init(&b, dataarray, sizeof(dataarray) - 1);
113	result = dns_rdata_totext(rdata, NULL, &b);
114	check_result(result, "dns_rdata_totext");
115	dataarray[isc_buffer_usedlength(&b)] = 0;
116	quotestring(dataarray, canondataarray);
117
118	snprintf(str, sizeof(str),
119		 "INSERT INTO %s (NAME, TTL, RDTYPE, RDATA)"
120		 " VALUES ('%s', %d, '%s', '%s')",
121		 dbtable, canonnamearray, ttl, canontypearray, canondataarray);
122	printf("%s\n", str);
123	res = PQexec(conn, str);
124	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
125		fprintf(stderr, "INSERT INTO command failed: %s\n",
126			PQresultErrorMessage(res));
127		PQclear(res);
128		closeandexit(1);
129	}
130	PQclear(res);
131}
132
133int
134main(int argc, char **argv) {
135	char *porigin, *zonefile;
136	dns_fixedname_t forigin, fname;
137	dns_name_t *origin, *name;
138	dns_db_t *db = NULL;
139	dns_dbiterator_t *dbiter;
140	dns_dbnode_t *node;
141	dns_rdatasetiter_t *rdsiter;
142	dns_rdataset_t rdataset;
143	dns_rdata_t rdata = DNS_RDATA_INIT;
144	isc_mem_t *mctx = NULL;
145	isc_entropy_t *ectx = NULL;
146	isc_buffer_t b;
147	isc_result_t result;
148	PGresult *res;
149
150	if (argc != 5) {
151		printf("usage: %s origin file dbname dbtable\n", argv[0]);
152		printf("Note that dbname must be an existing database.\n");
153		exit(1);
154	}
155
156	porigin = argv[1];
157	zonefile = argv[2];
158	dbname = argv[3];
159	dbtable = argv[4];
160
161	dns_result_register();
162
163	mctx = NULL;
164	result = isc_mem_create(0, 0, &mctx);
165	check_result(result, "isc_mem_create");
166
167	result = isc_entropy_create(mctx, &ectx);
168	check_result(result, "isc_entropy_create");
169
170	result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
171	check_result(result, "isc_hash_create");
172
173	isc_buffer_init(&b, porigin, strlen(porigin));
174	isc_buffer_add(&b, strlen(porigin));
175	dns_fixedname_init(&forigin);
176	origin = dns_fixedname_name(&forigin);
177	result = dns_name_fromtext(origin, &b, dns_rootname, 0, NULL);
178	check_result(result, "dns_name_fromtext");
179
180	db = NULL;
181	result = dns_db_create(mctx, "rbt", origin, dns_dbtype_zone,
182			       dns_rdataclass_in, 0, NULL, &db);
183	check_result(result, "dns_db_create");
184
185	result = dns_db_load(db, zonefile);
186	if (result == DNS_R_SEENINCLUDE)
187		result = ISC_R_SUCCESS;
188	check_result(result, "dns_db_load");
189
190	printf("Connecting to '%s'\n", dbname);
191	conn = PQsetdb(NULL, NULL, NULL, NULL, dbname);
192	if (PQstatus(conn) == CONNECTION_BAD) {
193		fprintf(stderr, "Connection to database '%s' failed: %s\n",
194			dbname, PQerrorMessage(conn));
195		closeandexit(1);
196	}
197
198	snprintf(str, sizeof(str),
199		 "DROP TABLE %s", dbtable);
200	printf("%s\n", str);
201	res = PQexec(conn, str);
202	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
203		fprintf(stderr, "DROP TABLE command failed: %s\n",
204			PQresultErrorMessage(res));
205	PQclear(res);
206
207	snprintf(str, sizeof(str), "BEGIN");
208	printf("%s\n", str);
209	res = PQexec(conn, str);
210	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
211		fprintf(stderr, "BEGIN command failed: %s\n",
212			PQresultErrorMessage(res));
213		PQclear(res);
214		closeandexit(1);
215	}
216	PQclear(res);
217
218	snprintf(str, sizeof(str),
219		 "CREATE TABLE %s "
220		 "(NAME TEXT, TTL INTEGER, RDTYPE TEXT, RDATA TEXT)",
221		 dbtable);
222	printf("%s\n", str);
223	res = PQexec(conn, str);
224	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
225		fprintf(stderr, "CREATE TABLE command failed: %s\n",
226			PQresultErrorMessage(res));
227		PQclear(res);
228		closeandexit(1);
229	}
230	PQclear(res);
231
232	dbiter = NULL;
233	result = dns_db_createiterator(db, 0, &dbiter);
234	check_result(result, "dns_db_createiterator()");
235
236	result = dns_dbiterator_first(dbiter);
237	check_result(result, "dns_dbiterator_first");
238
239	dns_fixedname_init(&fname);
240	name = dns_fixedname_name(&fname);
241	dns_rdataset_init(&rdataset);
242	dns_rdata_init(&rdata);
243
244	while (result == ISC_R_SUCCESS) {
245		node = NULL;
246		result = dns_dbiterator_current(dbiter, &node, name);
247		if (result == ISC_R_NOMORE)
248			break;
249		check_result(result, "dns_dbiterator_current");
250
251		rdsiter = NULL;
252		result = dns_db_allrdatasets(db, node, NULL, 0, &rdsiter);
253		check_result(result, "dns_db_allrdatasets");
254
255		result = dns_rdatasetiter_first(rdsiter);
256
257		while (result == ISC_R_SUCCESS) {
258			dns_rdatasetiter_current(rdsiter, &rdataset);
259			result = dns_rdataset_first(&rdataset);
260			check_result(result, "dns_rdataset_first");
261			while (result == ISC_R_SUCCESS) {
262				dns_rdataset_current(&rdataset, &rdata);
263				addrdata(name, rdataset.ttl, &rdata);
264				dns_rdata_reset(&rdata);
265				result = dns_rdataset_next(&rdataset);
266			}
267			dns_rdataset_disassociate(&rdataset);
268			result = dns_rdatasetiter_next(rdsiter);
269		}
270		dns_rdatasetiter_destroy(&rdsiter);
271		dns_db_detachnode(db, &node);
272		result = dns_dbiterator_next(dbiter);
273	}
274
275	snprintf(str, sizeof(str), "COMMIT TRANSACTION");
276	printf("%s\n", str);
277	res = PQexec(conn, str);
278	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
279		fprintf(stderr, "COMMIT command failed: %s\n",
280			PQresultErrorMessage(res));
281		PQclear(res);
282		closeandexit(1);
283	}
284	PQclear(res);
285	dns_dbiterator_destroy(&dbiter);
286	dns_db_detach(&db);
287	isc_hash_destroy();
288	isc_entropy_detach(&ectx);
289	isc_mem_destroy(&mctx);
290	closeandexit(0);
291	exit(0);
292}
293