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