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