1/*
2 * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id$ */
18
19/*! \file */
20
21#include <config.h>
22
23#include <atf-c.h>
24
25#include <unistd.h>
26#include <stdlib.h>
27
28#include <dns/db.h>
29#include <dns/dbiterator.h>
30#include <dns/name.h>
31
32#include "dnstest.h"
33
34/*
35 * Helper functions
36 */
37
38#define	BUFLEN		255
39#define	BIGBUFLEN	(64 * 1024)
40#define TEST_ORIGIN	"test"
41
42static isc_result_t
43setup_db(const char *testfile, dns_dbtype_t dbtype, dns_db_t **db) {
44	isc_result_t		result;
45	int			len;
46	char			origin[sizeof(TEST_ORIGIN)];
47	dns_name_t		dns_origin;
48	isc_buffer_t		source;
49	isc_buffer_t		target;
50	unsigned char		name_buf[BUFLEN];
51
52	strcpy(origin, TEST_ORIGIN);
53	len = strlen(origin);
54	isc_buffer_init(&source, origin, len);
55	isc_buffer_add(&source, len);
56	isc_buffer_setactive(&source, len);
57	isc_buffer_init(&target, name_buf, BUFLEN);
58	dns_name_init(&dns_origin, NULL);
59
60	result = dns_name_fromtext(&dns_origin, &source, dns_rootname,
61				   0, &target);
62	if (result != ISC_R_SUCCESS)
63		return(result);
64
65	result = dns_db_create(mctx, "rbt", &dns_origin, dbtype,
66			       dns_rdataclass_in, 0, NULL, db);
67	if (result != ISC_R_SUCCESS)
68		return (result);
69
70	/*
71	 * atf-run changes us to a /tmp directory, so tests
72	 * that access test data files must first chdir to the proper
73	 * location.
74	 */
75	if (chdir(TESTS) == -1)
76		return (ISC_R_FAILURE);
77
78	result = dns_db_load(*db, testfile);
79	return (result);
80}
81
82static isc_result_t
83make_name(const char *src, dns_name_t *name) {
84	isc_buffer_t b;
85	isc_buffer_init(&b, src, strlen(src));
86	isc_buffer_add(&b, strlen(src));
87	return (dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
88}
89
90/*
91 * Individual unit tests
92 */
93
94/* create: make sure we can create a dbiterator */
95static void
96test_create(const atf_tc_t *tc) {
97	isc_result_t result;
98	dns_db_t *db = NULL;
99	dns_dbiterator_t *iter = NULL;
100
101	result = dns_test_begin(NULL, ISC_FALSE);
102	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
103
104	result = setup_db(atf_tc_get_md_var(tc, "X-filename"),
105			  dns_dbtype_cache, &db);
106	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
107
108	result = dns_db_createiterator(db, 0, &iter);
109	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
110
111	dns_dbiterator_destroy(&iter);
112	dns_db_detach(&db);
113	dns_test_end();
114}
115
116ATF_TC(create);
117ATF_TC_HEAD(create, tc) {
118	atf_tc_set_md_var(tc, "descr", "create a database iterator");
119	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
120}
121ATF_TC_BODY(create, tc) {
122	test_create(tc);
123}
124
125ATF_TC(create_nsec3);
126ATF_TC_HEAD(create_nsec3, tc) {
127	atf_tc_set_md_var(tc, "descr", "create a database iterator (NSEC3)");
128	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
129}
130ATF_TC_BODY(create_nsec3, tc) {
131	test_create(tc);
132}
133
134/* walk: walk a database */
135static void
136test_walk(const atf_tc_t *tc) {
137	isc_result_t result;
138	dns_db_t *db = NULL;
139	dns_dbiterator_t *iter = NULL;
140	dns_dbnode_t *node = NULL;
141	dns_name_t *name;
142	dns_fixedname_t f;
143	int i = 0;
144
145	UNUSED(tc);
146
147	dns_fixedname_init(&f);
148	name = dns_fixedname_name(&f);
149
150	result = dns_test_begin(NULL, ISC_FALSE);
151	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
152
153	result = setup_db(atf_tc_get_md_var(tc, "X-filename"),
154			  dns_dbtype_cache, &db);
155	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
156
157	result = dns_db_createiterator(db, 0, &iter);
158	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
159
160	for (result = dns_dbiterator_first(iter);
161	     result == ISC_R_SUCCESS;
162	     result = dns_dbiterator_next(iter)) {
163		result = dns_dbiterator_current(iter, &node, name);
164		if (result == DNS_R_NEWORIGIN)
165			result = ISC_R_SUCCESS;
166		ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
167		dns_db_detachnode(db, &node);
168		i++;
169	}
170
171	ATF_CHECK_EQ(i, atoi(atf_tc_get_md_var(tc, "X-nodes")));
172
173	dns_dbiterator_destroy(&iter);
174	dns_db_detach(&db);
175	dns_test_end();
176}
177
178ATF_TC(walk);
179ATF_TC_HEAD(walk, tc) {
180	atf_tc_set_md_var(tc, "descr", "walk database");
181	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
182	atf_tc_set_md_var(tc, "X-nodes", "12");
183}
184ATF_TC_BODY(walk, tc) {
185	test_walk(tc);
186}
187
188ATF_TC(walk_nsec3);
189ATF_TC_HEAD(walk_nsec3, tc) {
190	atf_tc_set_md_var(tc, "descr", "walk database");
191	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
192	atf_tc_set_md_var(tc, "X-nodes", "33");
193}
194ATF_TC_BODY(walk_nsec3, tc) {
195	test_walk(tc);
196}
197
198/* reverse: walk database backwards */
199static void test_reverse(const atf_tc_t *tc) {
200	isc_result_t result;
201	dns_db_t *db = NULL;
202	dns_dbiterator_t *iter = NULL;
203	dns_dbnode_t *node = NULL;
204	dns_name_t *name;
205	dns_fixedname_t f;
206	int i = 0;
207
208	UNUSED(tc);
209
210	dns_fixedname_init(&f);
211	name = dns_fixedname_name(&f);
212
213	result = dns_test_begin(NULL, ISC_FALSE);
214	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
215
216	result = setup_db(atf_tc_get_md_var(tc, "X-filename"),
217			  dns_dbtype_cache, &db);
218	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
219
220	result = dns_db_createiterator(db, 0, &iter);
221	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
222
223	for (result = dns_dbiterator_last(iter);
224	     result == ISC_R_SUCCESS;
225	     result = dns_dbiterator_prev(iter)) {
226		result = dns_dbiterator_current(iter, &node, name);
227		if (result == DNS_R_NEWORIGIN)
228			result = ISC_R_SUCCESS;
229		ATF_CHECK_EQ(result, ISC_R_SUCCESS);
230		dns_db_detachnode(db, &node);
231		i++;
232	}
233
234	ATF_CHECK_EQ(i, 12);
235
236	dns_dbiterator_destroy(&iter);
237	dns_db_detach(&db);
238	dns_test_end();
239}
240
241ATF_TC(reverse);
242ATF_TC_HEAD(reverse, tc) {
243	atf_tc_set_md_var(tc, "descr", "walk database backwards");
244	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
245}
246ATF_TC_BODY(reverse, tc) {
247	test_reverse(tc);
248}
249
250ATF_TC(reverse_nsec3);
251ATF_TC_HEAD(reverse_nsec3, tc) {
252	atf_tc_set_md_var(tc, "descr", "walk database backwards");
253	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
254}
255ATF_TC_BODY(reverse_nsec3, tc) {
256	test_reverse(tc);
257}
258
259/* seek: walk database starting at a particular node */
260static void test_seek(const atf_tc_t *tc) {
261	isc_result_t result;
262	dns_db_t *db = NULL;
263	dns_dbiterator_t *iter = NULL;
264	dns_dbnode_t *node = NULL;
265	dns_name_t *name, *seekname;
266	dns_fixedname_t f1, f2;
267	int i = 0;
268
269	UNUSED(tc);
270
271	dns_fixedname_init(&f1);
272	name = dns_fixedname_name(&f1);
273	dns_fixedname_init(&f2);
274	seekname = dns_fixedname_name(&f2);
275
276	result = dns_test_begin(NULL, ISC_FALSE);
277	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
278
279	result = setup_db(atf_tc_get_md_var(tc, "X-filename"),
280			  dns_dbtype_cache, &db);
281	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
282
283	result = dns_db_createiterator(db, 0, &iter);
284	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
285
286	result = make_name("c." TEST_ORIGIN, seekname);
287	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
288
289	result = dns_dbiterator_seek(iter, seekname);
290	ATF_CHECK_EQ(result, ISC_R_SUCCESS);
291
292	while (result == ISC_R_SUCCESS) {
293		result = dns_dbiterator_current(iter, &node, name);
294		if (result == DNS_R_NEWORIGIN)
295			result = ISC_R_SUCCESS;
296		ATF_CHECK_EQ(result, ISC_R_SUCCESS);
297		dns_db_detachnode(db, &node);
298		result = dns_dbiterator_next(iter);
299		i++;
300	}
301
302	ATF_CHECK_EQ(i, atoi(atf_tc_get_md_var(tc, "X-nodes")));
303
304	dns_dbiterator_destroy(&iter);
305	dns_db_detach(&db);
306	dns_test_end();
307}
308
309ATF_TC(seek);
310ATF_TC_HEAD(seek, tc) {
311	atf_tc_set_md_var(tc, "descr", "walk database starting at "
312				       "a particular node");
313	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
314	atf_tc_set_md_var(tc, "X-nodes", "9");
315}
316ATF_TC_BODY(seek, tc) {
317	test_seek(tc);
318}
319
320ATF_TC(seek_nsec3);
321ATF_TC_HEAD(seek_nsec3, tc) {
322	atf_tc_set_md_var(tc, "descr", "walk database starting at "
323				       "a particular node");
324	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
325	atf_tc_set_md_var(tc, "X-nodes", "30");
326}
327ATF_TC_BODY(seek_nsec3, tc) {
328	test_seek(tc);
329}
330
331/*
332 * seek_emty: walk database starting at an empty nonterminal node
333 * (should fail)
334 */
335static void test_seek_empty(const atf_tc_t *tc) {
336	isc_result_t result;
337	dns_db_t *db = NULL;
338	dns_dbiterator_t *iter = NULL;
339	dns_name_t *seekname;
340	dns_fixedname_t f1;
341
342	UNUSED(tc);
343
344	dns_fixedname_init(&f1);
345	seekname = dns_fixedname_name(&f1);
346
347	result = dns_test_begin(NULL, ISC_FALSE);
348	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
349
350	result = setup_db(atf_tc_get_md_var(tc, "X-filename"),
351			  dns_dbtype_cache, &db);
352	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
353
354	result = dns_db_createiterator(db, 0, &iter);
355	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
356
357	result = make_name("d." TEST_ORIGIN, seekname);
358	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
359
360	result = dns_dbiterator_seek(iter, seekname);
361	ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
362
363	dns_dbiterator_destroy(&iter);
364	dns_db_detach(&db);
365	dns_test_end();
366}
367
368ATF_TC(seek_empty);
369ATF_TC_HEAD(seek_empty, tc) {
370	atf_tc_set_md_var(tc, "descr", "walk database starting at an "
371				       "empty nonterminal node");
372	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
373}
374ATF_TC_BODY(seek_empty, tc) {
375	test_seek_empty(tc);
376}
377
378ATF_TC(seek_empty_nsec3);
379ATF_TC_HEAD(seek_empty_nsec3, tc) {
380	atf_tc_set_md_var(tc, "descr", "walk database starting at an "
381				       "empty nonterminal node");
382	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
383}
384ATF_TC_BODY(seek_empty_nsec3, tc) {
385	test_seek_empty(tc);
386}
387
388/*
389 * seek_emty: walk database starting at an empty nonterminal node
390 * (should fail)
391 */
392static void test_seek_nx(const atf_tc_t *tc) {
393	isc_result_t result;
394	dns_db_t *db = NULL;
395	dns_dbiterator_t *iter = NULL;
396	dns_name_t *seekname;
397	dns_fixedname_t f1;
398
399	UNUSED(tc);
400
401	dns_fixedname_init(&f1);
402	seekname = dns_fixedname_name(&f1);
403
404	result = dns_test_begin(NULL, ISC_FALSE);
405	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
406
407	result = setup_db(atf_tc_get_md_var(tc, "X-filename"),
408			  dns_dbtype_cache, &db);
409	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
410
411	result = dns_db_createiterator(db, 0, &iter);
412	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
413
414	result = make_name("nonexistent." TEST_ORIGIN, seekname);
415	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
416
417	result = dns_dbiterator_seek(iter, seekname);
418	ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
419
420	dns_dbiterator_destroy(&iter);
421	dns_db_detach(&db);
422	dns_test_end();
423}
424
425ATF_TC(seek_nx);
426ATF_TC_HEAD(seek_nx, tc) {
427	atf_tc_set_md_var(tc, "descr", "attempt to walk database starting "
428				       "at a nonexistent node");
429	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
430}
431ATF_TC_BODY(seek_nx, tc) {
432	test_seek_nx(tc);
433}
434
435ATF_TC(seek_nx_nsec3);
436ATF_TC_HEAD(seek_nx_nsec3, tc) {
437	atf_tc_set_md_var(tc, "descr", "attempt to walk database starting "
438				       "at a nonexistent node");
439	atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
440}
441ATF_TC_BODY(seek_nx_nsec3, tc) {
442	test_seek_nx(tc);
443}
444
445/*
446 * Main
447 */
448ATF_TP_ADD_TCS(tp) {
449	ATF_TP_ADD_TC(tp, create);
450	ATF_TP_ADD_TC(tp, create_nsec3);
451	ATF_TP_ADD_TC(tp, walk);
452	ATF_TP_ADD_TC(tp, walk_nsec3);
453	ATF_TP_ADD_TC(tp, reverse);
454	ATF_TP_ADD_TC(tp, reverse_nsec3);
455	ATF_TP_ADD_TC(tp, seek);
456	ATF_TP_ADD_TC(tp, seek_nsec3);
457	ATF_TP_ADD_TC(tp, seek_empty);
458	ATF_TP_ADD_TC(tp, seek_empty_nsec3);
459	ATF_TP_ADD_TC(tp, seek_nx);
460	ATF_TP_ADD_TC(tp, seek_nx_nsec3);
461	return (atf_no_error());
462}
463
464/*
465 * XXX:
466 * dns_dbiterator API calls that are not yet part of this unit test:
467 *
468 * dns_dbiterator_pause
469 * dns_dbiterator_origin
470 * dns_dbiterator_setcleanmode
471 */
472