1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007, 2009, 2011  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2001  Internet Software Consortium.
6 *
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.
10 *
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.
18 */
19
20/* Id: rbt_test.c,v 1.52 2011/08/28 23:46:41 tbox Exp  */
21
22#include <config.h>
23
24#include <stdlib.h>
25
26#include <isc/commandline.h>
27#include <isc/mem.h>
28#include <isc/string.h>
29#include <isc/util.h>
30
31#include <dns/rbt.h>
32#include <dns/fixedname.h>
33#include <dns/result.h>
34
35char *progname;
36isc_mem_t *mctx;
37
38#define DNSNAMELEN 255
39
40static dns_name_t *
41create_name(char *s) {
42	int length;
43	isc_result_t result;
44	isc_buffer_t source, target;
45	static dns_name_t *name;
46
47	if (s == NULL || *s == '\0') {
48		printf("missing name argument\n");
49		return (NULL);
50	}
51
52	length = strlen(s);
53
54	isc_buffer_init(&source, s, length);
55	isc_buffer_add(&source, length);
56
57	/*
58	 * It isn't really necessary in this program to create individual
59	 * memory spaces for each name structure and its associated character
60	 * string.  It is done here to provide a relatively easy way to test
61	 * the callback from dns_rbt_deletename that is supposed to free the
62	 * data associated with a node.
63	 *
64	 * The buffer for the actual name will immediately follow the
65	 * name structure.
66	 */
67	name = isc_mem_get(mctx, sizeof(*name) + DNSNAMELEN);
68	if (name == NULL) {
69		printf("out of memory!\n");
70		return (NULL);
71	}
72
73	dns_name_init(name, NULL);
74	isc_buffer_init(&target, name + 1, DNSNAMELEN);
75
76	result = dns_name_fromtext(name, &source, dns_rootname, 0, &target);
77
78	if (result != ISC_R_SUCCESS) {
79		printf("dns_name_fromtext(%s) failed: %s\n",
80		       s, dns_result_totext(result));
81		return (NULL);
82	}
83
84	return (name);
85}
86
87static void
88delete_name(void *data, void *arg) {
89	dns_name_t *name;
90
91	UNUSED(arg);
92	name = data;
93	isc_mem_put(mctx, name, sizeof(*name) + DNSNAMELEN);
94}
95
96static void
97print_name(dns_name_t *name) {
98	isc_buffer_t target;
99	char buffer[1024];
100
101	isc_buffer_init(&target, buffer, sizeof(buffer));
102
103	/*
104	 * ISC_FALSE means absolute names have the final dot added.
105	 */
106	dns_name_totext(name, ISC_FALSE, &target);
107
108	printf("%.*s", (int)target.used, (char *)target.base);
109}
110
111static void
112detail(dns_rbt_t *rbt, dns_name_t *name) {
113	dns_name_t *foundname, *origin, *fullname;
114	dns_fixedname_t fixedfoundname, fixedorigin, fixedfullname;
115	dns_rbtnode_t *node1, *node2;
116	dns_rbtnodechain_t chain;
117	isc_result_t result;
118	isc_boolean_t nodes_should_match = ISC_FALSE;
119
120	dns_rbtnodechain_init(&chain, mctx);
121
122	dns_fixedname_init(&fixedorigin);
123	dns_fixedname_init(&fixedfullname);
124	dns_fixedname_init(&fixedfoundname);
125
126	origin = dns_fixedname_name(&fixedorigin);
127	fullname = dns_fixedname_name(&fixedfullname);
128	foundname = dns_fixedname_name(&fixedfoundname);
129
130	node1 = node2 = NULL;
131
132	printf("checking chain information for ");
133	print_name(name);
134	printf("\n");
135
136	result = dns_rbt_findnode(rbt, name, foundname, &node1, &chain,
137				  DNS_RBTFIND_EMPTYDATA, NULL, NULL);
138
139	switch (result) {
140	case ISC_R_SUCCESS:
141		printf("  found exact.");
142		nodes_should_match = ISC_TRUE;
143		break;
144	case DNS_R_PARTIALMATCH:
145		printf("  found parent.");
146		break;
147	case ISC_R_NOTFOUND:
148		printf("  name not found.");
149		break;
150	default:
151		printf("  unexpected result: %s\n", dns_result_totext(result));
152		return;
153	}
154
155	if (node1 != NULL && node1->data != NULL) {
156		printf("  data at node: ");
157		print_name(node1->data);
158	} else
159		printf("  no data at node.");
160
161	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
162		printf("\n  name from dns_rbt_findnode: ");
163		print_name(foundname);
164	}
165
166	result = dns_rbtnodechain_current(&chain, foundname, origin, &node2);
167
168	if (result == ISC_R_SUCCESS) {
169		printf("\n  name from dns_rbtnodechain_current: ");
170
171		result = dns_name_concatenate(foundname, origin,
172					      fullname, NULL);
173		if (result == ISC_R_SUCCESS)
174			print_name(fullname);
175		else
176			printf("%s\n", dns_result_totext(result));
177		printf("\n      (foundname = ");
178		print_name(foundname);
179		printf(", origin = ");
180		print_name(origin);
181		printf(")\n");
182		if (nodes_should_match && node1 != node2)
183			printf("  nodes returned from each function "
184			       "DO NOT match!\n");
185
186	} else
187		printf("\n  result from dns_rbtnodechain_current: %s\n",
188		       dns_result_totext(result));
189
190	printf("  level_matches = %d, level_count = %d\n",
191	       chain.level_matches, chain.level_count);
192}
193
194static void
195iterate(dns_rbt_t *rbt, isc_boolean_t forward) {
196	dns_name_t foundname, *origin;
197	dns_rbtnodechain_t chain;
198	dns_fixedname_t fixedorigin;
199	isc_result_t result;
200	isc_result_t (*move)(dns_rbtnodechain_t *chain, dns_name_t *name,
201			     dns_name_t *origin);
202
203	dns_rbtnodechain_init(&chain, mctx);
204
205	dns_name_init(&foundname, NULL);
206	dns_fixedname_init(&fixedorigin);
207	origin = dns_fixedname_name(&fixedorigin);
208
209	if (forward) {
210		printf("iterating forward\n" );
211		move = dns_rbtnodechain_next;
212
213		result = dns_rbtnodechain_first(&chain, rbt, &foundname,
214						origin);
215
216	} else {
217		printf("iterating backward\n" );
218		move = dns_rbtnodechain_prev;
219
220		result = dns_rbtnodechain_last(&chain, rbt, &foundname,
221					       origin);
222	}
223
224	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
225		printf("start not found!\n");
226
227	else {
228		for (;;) {
229			if (result == DNS_R_NEWORIGIN) {
230				printf("  new origin: ");
231				print_name(origin);
232				printf("\n");
233			}
234
235			if (result == ISC_R_SUCCESS ||
236			    result == DNS_R_NEWORIGIN) {
237				print_name(&foundname);
238				printf("\n");
239
240			} else {
241				if (result != ISC_R_NOMORE)
242				       printf("UNEXEPCTED ITERATION ERROR: %s",
243					      dns_result_totext(result));
244				break;
245			}
246
247			result = move(&chain, &foundname, origin);
248		}
249	}
250}
251
252
253#define CMDCHECK(s)	(strncasecmp(command, (s), length) == 0)
254#define PRINTERR(r)	if (r != ISC_R_SUCCESS) \
255				printf("... %s\n", dns_result_totext(r));
256
257int
258main(int argc, char **argv) {
259	char *command, *arg, buffer[1024];
260	const char *whitespace;
261	dns_name_t *name, *foundname;
262	dns_fixedname_t fixedname;
263	dns_rbt_t *rbt = NULL;
264	int length, ch;
265	isc_boolean_t show_final_mem = ISC_FALSE;
266	isc_result_t result;
267	void *data;
268
269	progname = strrchr(*argv, '/');
270	if (progname != NULL)
271		progname++;
272	else
273		progname = *argv;
274
275	while ((ch = isc_commandline_parse(argc, argv, "m")) != -1) {
276		switch (ch) {
277		case 'm':
278			show_final_mem = ISC_TRUE;
279			break;
280		}
281	}
282
283	argc -= isc_commandline_index;
284	argv += isc_commandline_index;
285	POST(argv);
286
287	if (argc > 1) {
288		printf("Usage: %s [-m]\n", progname);
289		exit(1);
290	}
291
292	setbuf(stdout, NULL);
293
294	/*
295	 * So isc_mem_stats() can report any allocation leaks.
296	 */
297	isc_mem_debugging = ISC_MEM_DEBUGRECORD;
298
299	result = isc_mem_create(0, 0, &mctx);
300	if (result != ISC_R_SUCCESS) {
301		printf("isc_mem_create: %s: exiting\n",
302		       dns_result_totext(result));
303		exit(1);
304	}
305
306	result = dns_rbt_create(mctx, delete_name, NULL, &rbt);
307	if (result != ISC_R_SUCCESS) {
308		printf("dns_rbt_create: %s: exiting\n",
309		       dns_result_totext(result));
310		exit(1);
311	}
312
313	whitespace = " \t";
314
315	while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
316		length = strlen(buffer);
317
318		if (buffer[length - 1] != '\n') {
319			printf("line to long (%lu max), ignored\n",
320			       (unsigned long)sizeof(buffer) - 2);
321			continue;
322		}
323
324		buffer[length - 1] = '\0';
325
326		command = buffer + strspn(buffer, whitespace);
327
328		if (*command == '#')
329			continue;
330
331		arg = strpbrk(command, whitespace);
332		if (arg != NULL) {
333			*arg++ = '\0';
334			arg += strspn(arg, whitespace);
335		}
336
337		length = strlen(command);
338		if (*command != '\0') {
339			if (CMDCHECK("add")) {
340				name = create_name(arg);
341				if (name != NULL) {
342					printf("adding name %s\n", arg);
343					result = dns_rbt_addname(rbt,
344								 name, name);
345					PRINTERR(result);
346				}
347
348			} else if (CMDCHECK("delete")) {
349				name = create_name(arg);
350				if (name != NULL) {
351					printf("deleting name %s\n", arg);
352					result = dns_rbt_deletename(rbt, name,
353								    ISC_FALSE);
354					PRINTERR(result);
355					delete_name(name, NULL);
356				}
357
358			} else if (CMDCHECK("nuke")) {
359				name = create_name(arg);
360				if (name != NULL) {
361					printf("nuking name %s "
362					       "and its descendants\n", arg);
363					result = dns_rbt_deletename(rbt, name,
364								    ISC_TRUE);
365					PRINTERR(result);
366					delete_name(name, NULL);
367				}
368
369			} else if (CMDCHECK("search")) {
370				name = create_name(arg);
371				if (name != NULL) {
372					printf("searching for name %s ... ",
373					       arg);
374
375					dns_fixedname_init(&fixedname);
376					foundname =
377						dns_fixedname_name(&fixedname);
378					data = NULL;
379
380					result = dns_rbt_findname(rbt, name, 0,
381								  foundname,
382								  &data);
383					switch (result) {
384					case ISC_R_SUCCESS:
385						printf("found exact: ");
386						print_name(data);
387						putchar('\n');
388						break;
389					case DNS_R_PARTIALMATCH:
390						printf("found parent: ");
391						print_name(data);
392						printf("\n\t(foundname: ");
393						print_name(foundname);
394						printf(")\n");
395						break;
396					case ISC_R_NOTFOUND:
397						printf("NOT FOUND!\n");
398						break;
399					case ISC_R_NOMEMORY:
400						printf("OUT OF MEMORY!\n");
401						break;
402					default:
403						printf("UNEXPECTED RESULT\n");
404					}
405
406					delete_name(name, NULL);
407				}
408
409			} else if (CMDCHECK("check")) {
410				/*
411				 * Or "chain".  I know, I know.  Lame name.
412				 * I was having a hard time thinking of a
413				 * name (especially one that did not have
414				 * a conflicting first letter with another
415				 * command) that would differentiate this
416				 * from the search command.
417				 *
418				 * But it is just a test program, eh?
419				 */
420				name = create_name(arg);
421				if (name != NULL) {
422					detail(rbt, name);
423
424					delete_name(name, NULL);
425				}
426
427			} else if (CMDCHECK("forward")) {
428				iterate(rbt, ISC_TRUE);
429
430			} else if (CMDCHECK("backward")) {
431				iterate(rbt, ISC_FALSE);
432
433			} else if (CMDCHECK("print")) {
434				if (arg == NULL || *arg == '\0')
435					dns_rbt_printall(rbt);
436				else
437					printf("usage: print\n");
438
439			} else if (CMDCHECK("quit")) {
440				if (arg == NULL || *arg == '\0')
441					break;
442				else
443					printf("usage: quit\n");
444			} else {
445				printf("a(dd) NAME, d(elete) NAME, "
446				       "s(earch) NAME, p(rint), or q(uit)\n");
447
448			}
449		}
450
451	}
452
453	dns_rbt_destroy(&rbt);
454
455	if (show_final_mem)
456		isc_mem_stats(mctx, stderr);
457
458	return (0);
459}
460