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 <stdlib.h>
15 #include <sys/param.h>
16 #include <sys/select.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 
21 #include <isc/app.h>
22 #include <isc/commandline.h>
23 #include <isc/managers.h>
24 #include <isc/mem.h>
25 #include <isc/print.h>
26 #include <isc/socket.h>
27 #include <isc/string.h>
28 #include <isc/task.h>
29 #include <isc/timer.h>
30 #include <isc/util.h>
31 
32 #include <dns/db.h>
33 #include <dns/fixedname.h>
34 #include <dns/rdataclass.h>
35 #include <dns/rdataset.h>
36 #include <dns/result.h>
37 #include <dns/zone.h>
38 
39 static int debug = 0;
40 static int quiet = 0;
41 static int stats = 0;
42 static isc_mem_t *mctx = NULL;
43 dns_zone_t *zone = NULL;
44 isc_nm_t *netmgr = NULL;
45 isc_taskmgr_t *taskmgr = NULL;
46 isc_timermgr_t *timermgr = NULL;
47 isc_socketmgr_t *socketmgr = NULL;
48 dns_zonemgr_t *zonemgr = NULL;
49 dns_zonetype_t zonetype = dns_zone_primary;
50 isc_sockaddr_t addr;
51 
52 #define ERRRET(result, function)                                        \
53 	do {                                                            \
54 		if (result != ISC_R_SUCCESS) {                          \
55 			fprintf(stderr, "%s() returned %s\n", function, \
56 				dns_result_totext(result));             \
57 			return;                                         \
58 		}                                                       \
59 	} while (0)
60 
61 #define ERRCONT(result, function)                               \
62 	if (result != ISC_R_SUCCESS) {                          \
63 		fprintf(stderr, "%s() returned %s\n", function, \
64 			dns_result_totext(result));             \
65 		continue;                                       \
66 	} else                                                  \
67 		(void)NULL
68 
69 static void
usage(void)70 usage(void) {
71 	fprintf(stderr, "usage: zone_test [-dqsSM] [-c class] [-f file] "
72 			"zone\n");
73 	exit(1);
74 }
75 
76 static void
setup(const char * zonename,const char * filename,const char * classname)77 setup(const char *zonename, const char *filename, const char *classname) {
78 	isc_result_t result;
79 	dns_rdataclass_t rdclass;
80 	isc_consttextregion_t region;
81 	isc_buffer_t buffer;
82 	dns_fixedname_t fixorigin;
83 	dns_name_t *origin;
84 	const char *rbt = "rbt";
85 
86 	if (debug) {
87 		fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
88 			zonename, filename, classname);
89 	}
90 	result = dns_zone_create(&zone, mctx);
91 	ERRRET(result, "dns_zone_new");
92 
93 	dns_zone_settype(zone, zonetype);
94 
95 	isc_buffer_constinit(&buffer, zonename, strlen(zonename));
96 	isc_buffer_add(&buffer, strlen(zonename));
97 	dns_fixedname_init(&fixorigin);
98 	result = dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer,
99 				   dns_rootname, 0, NULL);
100 	ERRRET(result, "dns_name_fromtext");
101 	origin = dns_fixedname_name(&fixorigin);
102 
103 	result = dns_zone_setorigin(zone, origin);
104 	ERRRET(result, "dns_zone_setorigin");
105 
106 	dns_zone_setdbtype(zone, 1, &rbt);
107 
108 	result = dns_zone_setfile(zone, filename, dns_masterformat_text,
109 				  &dns_master_style_default);
110 	ERRRET(result, "dns_zone_setfile");
111 
112 	region.base = classname;
113 	region.length = strlen(classname);
114 	result = dns_rdataclass_fromtext(&rdclass,
115 					 (isc_textregion_t *)(void *)&region);
116 	ERRRET(result, "dns_rdataclass_fromtext");
117 
118 	dns_zone_setclass(zone, rdclass);
119 
120 	if (zonetype == dns_zone_secondary) {
121 		dns_zone_setprimaries(zone, &addr, 1);
122 	}
123 
124 	result = dns_zone_load(zone, false);
125 	ERRRET(result, "dns_zone_load");
126 
127 	result = dns_zonemgr_managezone(zonemgr, zone);
128 	ERRRET(result, "dns_zonemgr_managezone");
129 }
130 
131 static void
print_rdataset(dns_name_t * name,dns_rdataset_t * rdataset)132 print_rdataset(dns_name_t *name, dns_rdataset_t *rdataset) {
133 	isc_buffer_t text;
134 	char t[1000];
135 	isc_result_t result;
136 	isc_region_t r;
137 
138 	isc_buffer_init(&text, t, sizeof(t));
139 	result = dns_rdataset_totext(rdataset, name, false, false, &text);
140 	isc_buffer_usedregion(&text, &r);
141 	if (result == ISC_R_SUCCESS) {
142 		printf("%.*s", (int)r.length, (char *)r.base);
143 	} else {
144 		printf("%s\n", dns_result_totext(result));
145 	}
146 }
147 
148 static void
query(void)149 query(void) {
150 	char buf[1024];
151 	dns_fixedname_t name;
152 	dns_fixedname_t found;
153 	dns_db_t *db;
154 	isc_buffer_t buffer;
155 	isc_result_t result;
156 	dns_rdataset_t rdataset;
157 	dns_rdataset_t sigset;
158 	fd_set rfdset = { { 0 } };
159 
160 	db = NULL;
161 	result = dns_zone_getdb(zone, &db);
162 	if (result != ISC_R_SUCCESS) {
163 		fprintf(stderr, "%s() returned %s\n", "dns_zone_getdb",
164 			dns_result_totext(result));
165 		return;
166 	}
167 
168 	dns_fixedname_init(&found);
169 	dns_rdataset_init(&rdataset);
170 	dns_rdataset_init(&sigset);
171 
172 	do {
173 		char *s;
174 		fprintf(stdout, "zone_test ");
175 		fflush(stdout);
176 		FD_ZERO(&rfdset);
177 		FD_SET(0, &rfdset);
178 		select(1, &rfdset, NULL, NULL, NULL);
179 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
180 			fprintf(stdout, "\n");
181 			break;
182 		}
183 		buf[sizeof(buf) - 1] = '\0';
184 
185 		s = strchr(buf, '\n');
186 		if (s != NULL) {
187 			*s = '\0';
188 		}
189 		s = strchr(buf, '\r');
190 		if (s != NULL) {
191 			*s = '\0';
192 		}
193 		if (strcmp(buf, "dump") == 0) {
194 			dns_zone_dumptostream(zone, stdout,
195 					      dns_masterformat_text,
196 					      &dns_master_style_default, 0);
197 			continue;
198 		}
199 		if (strlen(buf) == 0U) {
200 			continue;
201 		}
202 		dns_fixedname_init(&name);
203 		isc_buffer_init(&buffer, buf, strlen(buf));
204 		isc_buffer_add(&buffer, strlen(buf));
205 		result = dns_name_fromtext(dns_fixedname_name(&name), &buffer,
206 					   dns_rootname, 0, NULL);
207 		ERRCONT(result, "dns_name_fromtext");
208 
209 		result = dns_db_find(db, dns_fixedname_name(&name),
210 				     NULL /*version*/, dns_rdatatype_a,
211 				     0 /*options*/, 0 /*time*/, NULL /*nodep*/,
212 				     dns_fixedname_name(&found), &rdataset,
213 				     &sigset);
214 		fprintf(stderr, "%s() returned %s\n", "dns_db_find",
215 			dns_result_totext(result));
216 		switch (result) {
217 		case DNS_R_DELEGATION:
218 			print_rdataset(dns_fixedname_name(&found), &rdataset);
219 			break;
220 		case ISC_R_SUCCESS:
221 			print_rdataset(dns_fixedname_name(&name), &rdataset);
222 			break;
223 		default:
224 			break;
225 		}
226 
227 		if (dns_rdataset_isassociated(&rdataset)) {
228 			dns_rdataset_disassociate(&rdataset);
229 		}
230 		if (dns_rdataset_isassociated(&sigset)) {
231 			dns_rdataset_disassociate(&sigset);
232 		}
233 	} while (1);
234 	dns_rdataset_invalidate(&rdataset);
235 	dns_db_detach(&db);
236 }
237 
238 int
main(int argc,char ** argv)239 main(int argc, char **argv) {
240 	int c;
241 	char *filename = NULL;
242 	const char *classname = "IN";
243 
244 	while ((c = isc_commandline_parse(argc, argv, "cdf:m:qsMS")) != EOF) {
245 		switch (c) {
246 		case 'c':
247 			classname = isc_commandline_argument;
248 			break;
249 		case 'd':
250 			debug++;
251 			break;
252 		case 'f':
253 			if (filename != NULL) {
254 				usage();
255 			}
256 			filename = isc_commandline_argument;
257 			break;
258 		case 'm':
259 			memset(&addr, 0, sizeof(addr));
260 			addr.type.sin.sin_family = AF_INET;
261 			if (inet_pton(AF_INET, isc_commandline_argument,
262 				      &addr.type.sin.sin_addr) != 1) {
263 				fprintf(stderr, "bad master address '%s'\n",
264 					isc_commandline_argument);
265 				exit(1);
266 			}
267 			addr.type.sin.sin_port = htons(53);
268 			break;
269 		case 'q':
270 			quiet++;
271 			break;
272 		case 's':
273 			stats++;
274 			break;
275 		case 'S':
276 			zonetype = dns_zone_secondary;
277 			break;
278 		case 'M':
279 			zonetype = dns_zone_primary;
280 			break;
281 		default:
282 			usage();
283 		}
284 	}
285 
286 	if (argv[isc_commandline_index] == NULL) {
287 		usage();
288 	}
289 
290 	RUNTIME_CHECK(isc_app_start() == ISC_R_SUCCESS);
291 	isc_mem_create(&mctx);
292 	RUNTIME_CHECK(isc_managers_create(mctx, 2, 0, NULL, &taskmgr) ==
293 		      ISC_R_SUCCESS);
294 	RUNTIME_CHECK(isc_timermgr_create(mctx, &timermgr) == ISC_R_SUCCESS);
295 	RUNTIME_CHECK(isc_socketmgr_create(mctx, &socketmgr) == ISC_R_SUCCESS);
296 	RUNTIME_CHECK(dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr,
297 					 &zonemgr) == ISC_R_SUCCESS);
298 	if (filename == NULL) {
299 		filename = argv[isc_commandline_index];
300 	}
301 	setup(argv[isc_commandline_index], filename, classname);
302 	query();
303 	if (zone != NULL) {
304 		dns_zone_detach(&zone);
305 	}
306 	dns_zonemgr_shutdown(zonemgr);
307 	dns_zonemgr_detach(&zonemgr);
308 	isc_socketmgr_destroy(&socketmgr);
309 	isc_managers_destroy(&netmgr, &taskmgr);
310 	isc_timermgr_destroy(&timermgr);
311 	if (!quiet && stats) {
312 		isc_mem_stats(mctx, stdout);
313 	}
314 	isc_mem_destroy(&mctx);
315 
316 	return (0);
317 }
318