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