1/*
2 * Copyright (C) 2009, 2010  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: sample-update.c,v 1.10 2010/12/09 00:54:34 marka Exp $ */
18
19#include <config.h>
20
21#include <sys/types.h>
22#include <sys/socket.h>
23
24#include <netinet/in.h>
25
26#include <arpa/inet.h>
27
28#include <unistd.h>
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <netdb.h>
34
35#include <isc/buffer.h>
36#include <isc/lex.h>
37#include <isc/lib.h>
38#include <isc/mem.h>
39#include <isc/parseint.h>
40#include <isc/sockaddr.h>
41#include <isc/util.h>
42
43#include <dns/callbacks.h>
44#include <dns/client.h>
45#include <dns/fixedname.h>
46#include <dns/lib.h>
47#include <dns/name.h>
48#include <dns/rdata.h>
49#include <dns/rdataclass.h>
50#include <dns/rdatalist.h>
51#include <dns/rdataset.h>
52#include <dns/rdatastruct.h>
53#include <dns/rdatatype.h>
54#include <dns/result.h>
55#include <dns/secalg.h>
56#include <dns/tsec.h>
57
58#include <dst/dst.h>
59
60static dns_tsec_t *tsec = NULL;
61static const dns_rdataclass_t default_rdataclass = dns_rdataclass_in;
62static isc_bufferlist_t usedbuffers;
63static ISC_LIST(dns_rdatalist_t) usedrdatalists;
64
65static void setup_tsec(char *keyfile, isc_mem_t *mctx);
66static void update_addordelete(isc_mem_t *mctx, char *cmdline,
67			       isc_boolean_t isdelete, dns_name_t *name);
68static void evaluate_prereq(isc_mem_t *mctx, char *cmdline, dns_name_t *name);
69
70ISC_PLATFORM_NORETURN_PRE static void
71usage(void) ISC_PLATFORM_NORETURN_POST;
72
73static void
74usage(void) {
75	fprintf(stderr, "sample-update "
76		"[-a auth_server] "
77		"[-k keyfile] "
78		"[-p prerequisite] "
79		"[-r recursive_server] "
80		"[-z zonename] "
81		"(add|delete) \"name TTL RRtype RDATA\"\n");
82	exit(1);
83}
84
85int
86main(int argc, char *argv[]) {
87	int ch;
88	struct addrinfo hints, *res;
89	int gai_error;
90	dns_client_t *client = NULL;
91	char *zonenamestr = NULL;
92	char *keyfilename = NULL;
93	char *prereqstr = NULL;
94	isc_sockaddrlist_t auth_servers;
95	char *auth_server = NULL;
96	char *recursive_server = NULL;
97	isc_sockaddr_t sa_auth, sa_recursive;
98	isc_sockaddrlist_t rec_servers;
99	isc_result_t result;
100	isc_boolean_t isdelete;
101	isc_buffer_t b, *buf;
102	dns_fixedname_t zname0, pname0, uname0;
103	size_t namelen;
104	dns_name_t *zname = NULL, *uname, *pname;
105	dns_rdataset_t *rdataset;
106	dns_rdatalist_t *rdatalist;
107	dns_rdata_t *rdata;
108	dns_namelist_t updatelist, prereqlist, *prereqlistp = NULL;
109	isc_mem_t *umctx = NULL;
110
111	while ((ch = getopt(argc, argv, "a:k:p:r:z:")) != -1) {
112		switch (ch) {
113		case 'k':
114			keyfilename = optarg;
115			break;
116		case 'a':
117			auth_server = optarg;
118			break;
119		case 'p':
120			prereqstr = optarg;
121			break;
122		case 'r':
123			recursive_server = optarg;
124			break;
125		case 'z':
126			zonenamestr = optarg;
127			break;
128		default:
129			usage();
130		}
131	}
132
133	argc -= optind;
134	argv += optind;
135	if (argc < 2)
136		usage();
137
138	/* command line argument validation */
139	if (strcmp(argv[0], "delete") == 0)
140		isdelete = ISC_TRUE;
141	else if (strcmp(argv[0], "add") == 0)
142		isdelete = ISC_FALSE;
143	else {
144		fprintf(stderr, "invalid update command: %s\n", argv[0]);
145		exit(1);
146	}
147
148	if (auth_server == NULL && recursive_server == NULL) {
149		fprintf(stderr, "authoritative or recursive server "
150			"must be specified\n");
151		usage();
152	}
153
154	/* Initialization */
155	ISC_LIST_INIT(usedbuffers);
156	ISC_LIST_INIT(usedrdatalists);
157	ISC_LIST_INIT(prereqlist);
158	ISC_LIST_INIT(auth_servers);
159	isc_lib_register();
160	result = dns_lib_init();
161	if (result != ISC_R_SUCCESS) {
162		fprintf(stderr, "dns_lib_init failed: %d\n", result);
163		exit(1);
164	}
165	result = isc_mem_create(0, 0, &umctx);
166	if (result != ISC_R_SUCCESS) {
167		fprintf(stderr, "failed to crate mctx\n");
168		exit(1);
169	}
170
171	result = dns_client_create(&client, 0);
172	if (result != ISC_R_SUCCESS) {
173		fprintf(stderr, "dns_client_create failed: %d\n", result);
174		exit(1);
175	}
176
177	/* Set the authoritative server */
178	if (auth_server != NULL) {
179		memset(&hints, 0, sizeof(hints));
180		hints.ai_family = AF_UNSPEC;
181		hints.ai_socktype = SOCK_DGRAM;
182		hints.ai_protocol = IPPROTO_UDP;
183		hints.ai_flags = AI_NUMERICHOST;
184		gai_error = getaddrinfo(auth_server, "53", &hints, &res);
185		if (gai_error != 0) {
186			fprintf(stderr, "getaddrinfo failed: %s\n",
187				gai_strerror(gai_error));
188			exit(1);
189		}
190		INSIST(res->ai_addrlen <= sizeof(sa_auth.type));
191		memcpy(&sa_auth.type, res->ai_addr, res->ai_addrlen);
192		freeaddrinfo(res);
193		sa_auth.length = res->ai_addrlen;
194		ISC_LINK_INIT(&sa_auth, link);
195
196		ISC_LIST_APPEND(auth_servers, &sa_auth, link);
197	}
198
199	/* Set the recursive server */
200	if (recursive_server != NULL) {
201		memset(&hints, 0, sizeof(hints));
202		hints.ai_family = AF_UNSPEC;
203		hints.ai_socktype = SOCK_DGRAM;
204		hints.ai_protocol = IPPROTO_UDP;
205		hints.ai_flags = AI_NUMERICHOST;
206		gai_error = getaddrinfo(recursive_server, "53", &hints, &res);
207		if (gai_error != 0) {
208			fprintf(stderr, "getaddrinfo failed: %s\n",
209				gai_strerror(gai_error));
210			exit(1);
211		}
212		INSIST(res->ai_addrlen <= sizeof(sa_recursive.type));
213		memcpy(&sa_recursive.type, res->ai_addr, res->ai_addrlen);
214		freeaddrinfo(res);
215		sa_recursive.length = res->ai_addrlen;
216		ISC_LINK_INIT(&sa_recursive, link);
217		ISC_LIST_INIT(rec_servers);
218		ISC_LIST_APPEND(rec_servers, &sa_recursive, link);
219		result = dns_client_setservers(client, dns_rdataclass_in,
220					       NULL, &rec_servers);
221		if (result != ISC_R_SUCCESS) {
222			fprintf(stderr, "set server failed: %d\n", result);
223			exit(1);
224		}
225	}
226
227	/* Construct zone name */
228	zname = NULL;
229	if (zonenamestr != NULL) {
230		namelen = strlen(zonenamestr);
231		isc_buffer_init(&b, zonenamestr, namelen);
232		isc_buffer_add(&b, namelen);
233		dns_fixedname_init(&zname0);
234		zname = dns_fixedname_name(&zname0);
235		result = dns_name_fromtext(zname, &b, dns_rootname, 0, NULL);
236		if (result != ISC_R_SUCCESS)
237			fprintf(stderr, "failed to convert zone name: %d\n",
238				result);
239	}
240
241	/* Construct prerequisite name (if given) */
242	if (prereqstr != NULL) {
243		dns_fixedname_init(&pname0);
244		pname = dns_fixedname_name(&pname0);
245		evaluate_prereq(umctx, prereqstr, pname);
246		ISC_LIST_APPEND(prereqlist, pname, link);
247		prereqlistp = &prereqlist;
248	}
249
250	/* Construct update name */
251	ISC_LIST_INIT(updatelist);
252	dns_fixedname_init(&uname0);
253	uname = dns_fixedname_name(&uname0);
254	update_addordelete(umctx, argv[1], isdelete, uname);
255	ISC_LIST_APPEND(updatelist, uname, link);
256
257	/* Set up TSIG/SIG(0) key (if given) */
258	if (keyfilename != NULL)
259		setup_tsec(keyfilename, umctx);
260
261	/* Perform update */
262	result = dns_client_update(client,
263				   default_rdataclass, /* XXX: fixed */
264				   zname, prereqlistp, &updatelist,
265				   (auth_server == NULL) ? NULL :
266				   &auth_servers, tsec, 0);
267	if (result != ISC_R_SUCCESS) {
268		fprintf(stderr,
269			"update failed: %s\n", dns_result_totext(result));
270	} else
271		fprintf(stderr, "update succeeded\n");
272
273	/* Cleanup */
274	while ((pname = ISC_LIST_HEAD(prereqlist)) != NULL) {
275		while ((rdataset = ISC_LIST_HEAD(pname->list)) != NULL) {
276			ISC_LIST_UNLINK(pname->list, rdataset, link);
277			dns_rdataset_disassociate(rdataset);
278			isc_mem_put(umctx, rdataset, sizeof(*rdataset));
279		}
280		ISC_LIST_UNLINK(prereqlist, pname, link);
281	}
282	while ((uname = ISC_LIST_HEAD(updatelist)) != NULL) {
283		while ((rdataset = ISC_LIST_HEAD(uname->list)) != NULL) {
284			ISC_LIST_UNLINK(uname->list, rdataset, link);
285			dns_rdataset_disassociate(rdataset);
286			isc_mem_put(umctx, rdataset, sizeof(*rdataset));
287		}
288		ISC_LIST_UNLINK(updatelist, uname, link);
289	}
290	while ((rdatalist = ISC_LIST_HEAD(usedrdatalists)) != NULL) {
291		while ((rdata = ISC_LIST_HEAD(rdatalist->rdata)) != NULL) {
292			ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
293			isc_mem_put(umctx, rdata, sizeof(*rdata));
294		}
295		ISC_LIST_UNLINK(usedrdatalists, rdatalist, link);
296		isc_mem_put(umctx, rdatalist, sizeof(*rdatalist));
297	}
298	while ((buf = ISC_LIST_HEAD(usedbuffers)) != NULL) {
299		ISC_LIST_UNLINK(usedbuffers, buf, link);
300		isc_buffer_free(&buf);
301	}
302	if (tsec != NULL)
303		dns_tsec_destroy(&tsec);
304	isc_mem_destroy(&umctx);
305	dns_client_destroy(&client);
306	dns_lib_shutdown();
307
308	exit(0);
309}
310
311/*
312 *  Subroutines borrowed from nsupdate.c
313 */
314#define MAXWIRE (64 * 1024)
315#define TTL_MAX 2147483647U	/* Maximum signed 32 bit integer. */
316
317static char *
318nsu_strsep(char **stringp, const char *delim) {
319	char *string = *stringp;
320	char *s;
321	const char *d;
322	char sc, dc;
323
324	if (string == NULL)
325		return (NULL);
326
327	for (; *string != '\0'; string++) {
328		sc = *string;
329		for (d = delim; (dc = *d) != '\0'; d++) {
330			if (sc == dc)
331				break;
332		}
333		if (dc == 0)
334			break;
335	}
336
337	for (s = string; *s != '\0'; s++) {
338		sc = *s;
339		for (d = delim; (dc = *d) != '\0'; d++) {
340			if (sc == dc) {
341				*s++ = '\0';
342				*stringp = s;
343				return (string);
344			}
345		}
346	}
347	*stringp = NULL;
348	return (string);
349}
350
351static void
352fatal(const char *format, ...) {
353	va_list args;
354
355	va_start(args, format);
356	vfprintf(stderr, format, args);
357	va_end(args);
358	fprintf(stderr, "\n");
359	exit(1);
360}
361
362static inline void
363check_result(isc_result_t result, const char *msg) {
364	if (result != ISC_R_SUCCESS)
365		fatal("%s: %s", msg, isc_result_totext(result));
366}
367
368static void
369parse_name(char **cmdlinep, dns_name_t *name) {
370	isc_result_t result;
371	char *word;
372	isc_buffer_t source;
373
374	word = nsu_strsep(cmdlinep, " \t\r\n");
375	if (*word == 0) {
376		fprintf(stderr, "could not read owner name\n");
377		exit(1);
378	}
379
380	isc_buffer_init(&source, word, strlen(word));
381	isc_buffer_add(&source, strlen(word));
382	result = dns_name_fromtext(name, &source, dns_rootname, 0, NULL);
383	check_result(result, "dns_name_fromtext");
384	isc_buffer_invalidate(&source);
385}
386
387static void
388parse_rdata(isc_mem_t *mctx, char **cmdlinep, dns_rdataclass_t rdataclass,
389	    dns_rdatatype_t rdatatype, dns_rdata_t *rdata)
390{
391	char *cmdline = *cmdlinep;
392	isc_buffer_t source, *buf = NULL, *newbuf = NULL;
393	isc_region_t r;
394	isc_lex_t *lex = NULL;
395	dns_rdatacallbacks_t callbacks;
396	isc_result_t result;
397
398	while (cmdline != NULL && *cmdline != 0 &&
399	       isspace((unsigned char)*cmdline))
400		cmdline++;
401
402	if (cmdline != NULL && *cmdline != 0) {
403		dns_rdatacallbacks_init(&callbacks);
404		result = isc_lex_create(mctx, strlen(cmdline), &lex);
405		check_result(result, "isc_lex_create");
406		isc_buffer_init(&source, cmdline, strlen(cmdline));
407		isc_buffer_add(&source, strlen(cmdline));
408		result = isc_lex_openbuffer(lex, &source);
409		check_result(result, "isc_lex_openbuffer");
410		result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
411		check_result(result, "isc_buffer_allocate");
412		result = dns_rdata_fromtext(rdata, rdataclass, rdatatype, lex,
413					    dns_rootname, 0, mctx, buf,
414					    &callbacks);
415		isc_lex_destroy(&lex);
416		if (result == ISC_R_SUCCESS) {
417			isc_buffer_usedregion(buf, &r);
418			result = isc_buffer_allocate(mctx, &newbuf, r.length);
419			check_result(result, "isc_buffer_allocate");
420			isc_buffer_putmem(newbuf, r.base, r.length);
421			isc_buffer_usedregion(newbuf, &r);
422			dns_rdata_reset(rdata);
423			dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
424			isc_buffer_free(&buf);
425			ISC_LIST_APPEND(usedbuffers, newbuf, link);
426		} else {
427			fprintf(stderr, "invalid rdata format: %s\n",
428				isc_result_totext(result));
429			isc_buffer_free(&buf);
430			exit(1);
431		}
432	} else {
433		rdata->flags = DNS_RDATA_UPDATE;
434	}
435	*cmdlinep = cmdline;
436}
437
438static void
439update_addordelete(isc_mem_t *mctx, char *cmdline, isc_boolean_t isdelete,
440		   dns_name_t *name)
441{
442	isc_result_t result;
443	isc_uint32_t ttl;
444	char *word;
445	dns_rdataclass_t rdataclass;
446	dns_rdatatype_t rdatatype;
447	dns_rdata_t *rdata = NULL;
448	dns_rdatalist_t *rdatalist = NULL;
449	dns_rdataset_t *rdataset = NULL;
450	isc_textregion_t region;
451
452	/*
453	 * Read the owner name.
454	 */
455	parse_name(&cmdline, name);
456
457	rdata = isc_mem_get(mctx, sizeof(*rdata));
458	if (rdata == NULL) {
459		fprintf(stderr, "memory allocation for rdata failed\n");
460		exit(1);
461	}
462	dns_rdata_init(rdata);
463
464	/*
465	 * If this is an add, read the TTL and verify that it's in range.
466	 * If it's a delete, ignore a TTL if present (for compatibility).
467	 */
468	word = nsu_strsep(&cmdline, " \t\r\n");
469	if (word == NULL || *word == 0) {
470		if (!isdelete) {
471			fprintf(stderr, "could not read owner ttl\n");
472			exit(1);
473		}
474		else {
475			ttl = 0;
476			rdataclass = dns_rdataclass_any;
477			rdatatype = dns_rdatatype_any;
478			rdata->flags = DNS_RDATA_UPDATE;
479			goto doneparsing;
480		}
481	}
482	result = isc_parse_uint32(&ttl, word, 10);
483	if (result != ISC_R_SUCCESS) {
484		if (isdelete) {
485			ttl = 0;
486			goto parseclass;
487		} else {
488			fprintf(stderr, "ttl '%s': %s\n", word,
489				isc_result_totext(result));
490			exit(1);
491		}
492	}
493
494	if (isdelete)
495		ttl = 0;
496	else if (ttl > TTL_MAX) {
497		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
498			word, TTL_MAX);
499		exit(1);
500	}
501
502	/*
503	 * Read the class or type.
504	 */
505	word = nsu_strsep(&cmdline, " \t\r\n");
506 parseclass:
507	if (word == NULL || *word == 0) {
508		if (isdelete) {
509			rdataclass = dns_rdataclass_any;
510			rdatatype = dns_rdatatype_any;
511			rdata->flags = DNS_RDATA_UPDATE;
512		goto doneparsing;
513		} else {
514			fprintf(stderr, "could not read class or type\n");
515			exit(1);
516		}
517	}
518	region.base = word;
519	region.length = strlen(word);
520	result = dns_rdataclass_fromtext(&rdataclass, &region);
521	if (result == ISC_R_SUCCESS) {
522		/*
523		 * Now read the type.
524		 */
525		word = nsu_strsep(&cmdline, " \t\r\n");
526		if (word == NULL || *word == 0) {
527			if (isdelete) {
528				rdataclass = dns_rdataclass_any;
529				rdatatype = dns_rdatatype_any;
530				rdata->flags = DNS_RDATA_UPDATE;
531				goto doneparsing;
532			} else {
533				fprintf(stderr, "could not read type\n");
534				exit(1);
535			}
536		}
537		region.base = word;
538		region.length = strlen(word);
539		result = dns_rdatatype_fromtext(&rdatatype, &region);
540		if (result != ISC_R_SUCCESS) {
541			fprintf(stderr, "'%s' is not a valid type: %s\n",
542				word, isc_result_totext(result));
543			exit(1);
544		}
545	} else {
546		rdataclass = default_rdataclass;
547		result = dns_rdatatype_fromtext(&rdatatype, &region);
548		if (result != ISC_R_SUCCESS) {
549			fprintf(stderr, "'%s' is not a valid class or type: "
550				"%s\n", word, isc_result_totext(result));
551			exit(1);
552		}
553	}
554
555	parse_rdata(mctx, &cmdline, rdataclass, rdatatype, rdata);
556
557	if (isdelete) {
558		if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
559			rdataclass = dns_rdataclass_any;
560		else
561			rdataclass = dns_rdataclass_none;
562	} else {
563		if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
564			fprintf(stderr, "could not read rdata\n");
565			exit(1);
566		}
567	}
568
569 doneparsing:
570
571	rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
572	if (rdatalist == NULL) {
573		fprintf(stderr, "memory allocation for rdatalist failed\n");
574		exit(1);
575	}
576	dns_rdatalist_init(rdatalist);
577	rdatalist->type = rdatatype;
578	rdatalist->rdclass = rdataclass;
579	rdatalist->covers = rdatatype;
580	rdatalist->ttl = (dns_ttl_t)ttl;
581	ISC_LIST_INIT(rdatalist->rdata);
582	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
583	ISC_LIST_APPEND(usedrdatalists, rdatalist, link);
584
585	rdataset = isc_mem_get(mctx, sizeof(*rdataset));
586	if (rdataset == NULL) {
587		fprintf(stderr, "memory allocation for rdataset failed\n");
588		exit(1);
589	}
590	dns_rdataset_init(rdataset);
591	dns_rdatalist_tordataset(rdatalist, rdataset);
592	ISC_LIST_INIT(name->list);
593	ISC_LIST_APPEND(name->list, rdataset, link);
594}
595
596static void
597make_prereq(isc_mem_t *mctx, char *cmdline, isc_boolean_t ispositive,
598	    isc_boolean_t isrrset, dns_name_t *name)
599{
600	isc_result_t result;
601	char *word;
602	isc_textregion_t region;
603	dns_rdataset_t *rdataset = NULL;
604	dns_rdatalist_t *rdatalist = NULL;
605	dns_rdataclass_t rdataclass;
606	dns_rdatatype_t rdatatype;
607	dns_rdata_t *rdata = NULL;
608
609	/*
610	 * Read the owner name
611	 */
612	parse_name(&cmdline, name);
613
614	/*
615	 * If this is an rrset prereq, read the class or type.
616	 */
617	if (isrrset) {
618		word = nsu_strsep(&cmdline, " \t\r\n");
619		if (word == NULL || *word == 0) {
620			fprintf(stderr, "could not read class or type\n");
621			exit(1);
622		}
623		region.base = word;
624		region.length = strlen(word);
625		result = dns_rdataclass_fromtext(&rdataclass, &region);
626		if (result == ISC_R_SUCCESS) {
627			/*
628			 * Now read the type.
629			 */
630			word = nsu_strsep(&cmdline, " \t\r\n");
631			if (word == NULL || *word == 0) {
632				fprintf(stderr, "could not read type\n");
633				exit(1);
634			}
635			region.base = word;
636			region.length = strlen(word);
637			result = dns_rdatatype_fromtext(&rdatatype, &region);
638			if (result != ISC_R_SUCCESS) {
639				fprintf(stderr, "invalid type: %s\n", word);
640				exit(1);
641			}
642		} else {
643			rdataclass = default_rdataclass;
644			result = dns_rdatatype_fromtext(&rdatatype, &region);
645			if (result != ISC_R_SUCCESS) {
646				fprintf(stderr, "invalid type: %s\n", word);
647				exit(1);
648			}
649		}
650	} else
651		rdatatype = dns_rdatatype_any;
652
653	rdata = isc_mem_get(mctx, sizeof(*rdata));
654	if (rdata == NULL) {
655		fprintf(stderr, "memory allocation for rdata failed\n");
656		exit(1);
657	}
658	dns_rdata_init(rdata);
659
660	if (isrrset && ispositive)
661		parse_rdata(mctx, &cmdline, rdataclass, rdatatype, rdata);
662	else
663		rdata->flags = DNS_RDATA_UPDATE;
664
665	rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
666	if (rdatalist == NULL) {
667		fprintf(stderr, "memory allocation for rdatalist failed\n");
668		exit(1);
669	}
670	dns_rdatalist_init(rdatalist);
671	rdatalist->type = rdatatype;
672	if (ispositive) {
673		if (isrrset && rdata->data != NULL)
674			rdatalist->rdclass = rdataclass;
675		else
676			rdatalist->rdclass = dns_rdataclass_any;
677	} else
678		rdatalist->rdclass = dns_rdataclass_none;
679	rdatalist->covers = 0;
680	rdatalist->ttl = 0;
681	rdata->rdclass = rdatalist->rdclass;
682	rdata->type = rdatatype;
683	ISC_LIST_INIT(rdatalist->rdata);
684	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
685	ISC_LIST_APPEND(usedrdatalists, rdatalist, link);
686
687	rdataset = isc_mem_get(mctx, sizeof(*rdataset));
688	if (rdataset == NULL) {
689		fprintf(stderr, "memory allocation for rdataset failed\n");
690		exit(1);
691	}
692	dns_rdataset_init(rdataset);
693	dns_rdatalist_tordataset(rdatalist, rdataset);
694	ISC_LIST_INIT(name->list);
695	ISC_LIST_APPEND(name->list, rdataset, link);
696}
697
698static void
699evaluate_prereq(isc_mem_t *mctx, char *cmdline, dns_name_t *name) {
700	char *word;
701	isc_boolean_t ispositive, isrrset;
702
703	word = nsu_strsep(&cmdline, " \t\r\n");
704	if (word == NULL || *word == 0) {
705		fprintf(stderr, "could not read operation code\n");
706		exit(1);
707	}
708	if (strcasecmp(word, "nxdomain") == 0) {
709		ispositive = ISC_FALSE;
710		isrrset = ISC_FALSE;
711	} else if (strcasecmp(word, "yxdomain") == 0) {
712		ispositive = ISC_TRUE;
713		isrrset = ISC_FALSE;
714	} else if (strcasecmp(word, "nxrrset") == 0) {
715		ispositive = ISC_FALSE;
716		isrrset = ISC_TRUE;
717	} else if (strcasecmp(word, "yxrrset") == 0) {
718		ispositive = ISC_TRUE;
719		isrrset = ISC_TRUE;
720	} else {
721		fprintf(stderr, "incorrect operation code: %s\n", word);
722		exit(1);
723	}
724
725	make_prereq(mctx, cmdline, ispositive, isrrset, name);
726}
727
728static void
729setup_tsec(char *keyfile, isc_mem_t *mctx) {
730	dst_key_t *dstkey = NULL;
731	isc_result_t result;
732	dns_tsectype_t tsectype;
733
734	result = dst_key_fromnamedfile(keyfile, NULL,
735				       DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
736				       &dstkey);
737	if (result != ISC_R_SUCCESS) {
738		fprintf(stderr, "could not read key from %s: %s\n",
739			keyfile, isc_result_totext(result));
740		exit(1);
741	}
742
743	if (dst_key_alg(dstkey) == DST_ALG_HMACMD5)
744		tsectype = dns_tsectype_tsig;
745	else
746		tsectype = dns_tsectype_sig0;
747
748	result = dns_tsec_create(mctx, tsectype, dstkey, &tsec);
749	dst_key_free(&dstkey);
750	if (result != ISC_R_SUCCESS) {
751		fprintf(stderr, "could not create tsec: %s\n",
752			isc_result_totext(result));
753		exit(1);
754	}
755}
756