1224090Sdougb/*
2262706Serwin * Copyright (C) 2009, 2010, 2012-2014  Internet Systems Consortium, Inc. ("ISC")
3224090Sdougb *
4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any
5224090Sdougb * purpose with or without fee is hereby granted, provided that the above
6224090Sdougb * copyright notice and this permission notice appear in all copies.
7224090Sdougb *
8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10224090Sdougb * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14224090Sdougb * PERFORMANCE OF THIS SOFTWARE.
15224090Sdougb */
16224090Sdougb
17234010Sdougb/* $Id: sample-update.c,v 1.10 2010/12/09 00:54:34 marka Exp $ */
18224090Sdougb
19224090Sdougb#include <config.h>
20224090Sdougb
21224090Sdougb#include <sys/types.h>
22224090Sdougb#include <sys/socket.h>
23224090Sdougb
24224090Sdougb#include <netinet/in.h>
25224090Sdougb
26224090Sdougb#include <arpa/inet.h>
27224090Sdougb
28224090Sdougb#include <unistd.h>
29224090Sdougb#include <ctype.h>
30224090Sdougb#include <stdio.h>
31224090Sdougb#include <stdlib.h>
32224090Sdougb#include <string.h>
33224090Sdougb#include <netdb.h>
34224090Sdougb
35224090Sdougb#include <isc/buffer.h>
36224090Sdougb#include <isc/lex.h>
37224090Sdougb#include <isc/lib.h>
38224090Sdougb#include <isc/mem.h>
39224090Sdougb#include <isc/parseint.h>
40224090Sdougb#include <isc/sockaddr.h>
41224090Sdougb#include <isc/util.h>
42224090Sdougb
43224090Sdougb#include <dns/callbacks.h>
44224090Sdougb#include <dns/client.h>
45224090Sdougb#include <dns/fixedname.h>
46224090Sdougb#include <dns/lib.h>
47224090Sdougb#include <dns/name.h>
48224090Sdougb#include <dns/rdata.h>
49224090Sdougb#include <dns/rdataclass.h>
50224090Sdougb#include <dns/rdatalist.h>
51224090Sdougb#include <dns/rdataset.h>
52224090Sdougb#include <dns/rdatastruct.h>
53224090Sdougb#include <dns/rdatatype.h>
54224090Sdougb#include <dns/result.h>
55224090Sdougb#include <dns/secalg.h>
56224090Sdougb#include <dns/tsec.h>
57224090Sdougb
58224090Sdougb#include <dst/dst.h>
59224090Sdougb
60224090Sdougbstatic dns_tsec_t *tsec = NULL;
61224090Sdougbstatic const dns_rdataclass_t default_rdataclass = dns_rdataclass_in;
62224090Sdougbstatic isc_bufferlist_t usedbuffers;
63224090Sdougbstatic ISC_LIST(dns_rdatalist_t) usedrdatalists;
64224090Sdougb
65224090Sdougbstatic void setup_tsec(char *keyfile, isc_mem_t *mctx);
66224090Sdougbstatic void update_addordelete(isc_mem_t *mctx, char *cmdline,
67224090Sdougb			       isc_boolean_t isdelete, dns_name_t *name);
68224090Sdougbstatic void evaluate_prereq(isc_mem_t *mctx, char *cmdline, dns_name_t *name);
69224090Sdougb
70224090SdougbISC_PLATFORM_NORETURN_PRE static void
71224090Sdougbusage(void) ISC_PLATFORM_NORETURN_POST;
72224090Sdougb
73224090Sdougbstatic void
74224090Sdougbusage(void) {
75224090Sdougb	fprintf(stderr, "sample-update "
76224090Sdougb		"[-a auth_server] "
77224090Sdougb		"[-k keyfile] "
78224090Sdougb		"[-p prerequisite] "
79224090Sdougb		"[-r recursive_server] "
80224090Sdougb		"[-z zonename] "
81224090Sdougb		"(add|delete) \"name TTL RRtype RDATA\"\n");
82224090Sdougb	exit(1);
83224090Sdougb}
84224090Sdougb
85224090Sdougbint
86224090Sdougbmain(int argc, char *argv[]) {
87224090Sdougb	int ch;
88224090Sdougb	struct addrinfo hints, *res;
89224090Sdougb	int gai_error;
90224090Sdougb	dns_client_t *client = NULL;
91224090Sdougb	char *zonenamestr = NULL;
92224090Sdougb	char *keyfilename = NULL;
93224090Sdougb	char *prereqstr = NULL;
94224090Sdougb	isc_sockaddrlist_t auth_servers;
95224090Sdougb	char *auth_server = NULL;
96224090Sdougb	char *recursive_server = NULL;
97224090Sdougb	isc_sockaddr_t sa_auth, sa_recursive;
98224090Sdougb	isc_sockaddrlist_t rec_servers;
99224090Sdougb	isc_result_t result;
100224090Sdougb	isc_boolean_t isdelete;
101224090Sdougb	isc_buffer_t b, *buf;
102224090Sdougb	dns_fixedname_t zname0, pname0, uname0;
103224090Sdougb	size_t namelen;
104224090Sdougb	dns_name_t *zname = NULL, *uname, *pname;
105224090Sdougb	dns_rdataset_t *rdataset;
106224090Sdougb	dns_rdatalist_t *rdatalist;
107224090Sdougb	dns_rdata_t *rdata;
108224090Sdougb	dns_namelist_t updatelist, prereqlist, *prereqlistp = NULL;
109224090Sdougb	isc_mem_t *umctx = NULL;
110224090Sdougb
111224090Sdougb	while ((ch = getopt(argc, argv, "a:k:p:r:z:")) != -1) {
112224090Sdougb		switch (ch) {
113224090Sdougb		case 'k':
114224090Sdougb			keyfilename = optarg;
115224090Sdougb			break;
116224090Sdougb		case 'a':
117224090Sdougb			auth_server = optarg;
118224090Sdougb			break;
119224090Sdougb		case 'p':
120224090Sdougb			prereqstr = optarg;
121224090Sdougb			break;
122224090Sdougb		case 'r':
123224090Sdougb			recursive_server = optarg;
124224090Sdougb			break;
125224090Sdougb		case 'z':
126224090Sdougb			zonenamestr = optarg;
127224090Sdougb			break;
128224090Sdougb		default:
129224090Sdougb			usage();
130224090Sdougb		}
131224090Sdougb	}
132224090Sdougb
133224090Sdougb	argc -= optind;
134224090Sdougb	argv += optind;
135224090Sdougb	if (argc < 2)
136224090Sdougb		usage();
137224090Sdougb
138224090Sdougb	/* command line argument validation */
139224090Sdougb	if (strcmp(argv[0], "delete") == 0)
140224090Sdougb		isdelete = ISC_TRUE;
141224090Sdougb	else if (strcmp(argv[0], "add") == 0)
142224090Sdougb		isdelete = ISC_FALSE;
143224090Sdougb	else {
144224090Sdougb		fprintf(stderr, "invalid update command: %s\n", argv[0]);
145224090Sdougb		exit(1);
146224090Sdougb	}
147224090Sdougb
148224090Sdougb	if (auth_server == NULL && recursive_server == NULL) {
149224090Sdougb		fprintf(stderr, "authoritative or recursive server "
150224090Sdougb			"must be specified\n");
151224090Sdougb		usage();
152224090Sdougb	}
153224090Sdougb
154224090Sdougb	/* Initialization */
155224090Sdougb	ISC_LIST_INIT(usedbuffers);
156224090Sdougb	ISC_LIST_INIT(usedrdatalists);
157224090Sdougb	ISC_LIST_INIT(prereqlist);
158224090Sdougb	ISC_LIST_INIT(auth_servers);
159224090Sdougb	isc_lib_register();
160224090Sdougb	result = dns_lib_init();
161224090Sdougb	if (result != ISC_R_SUCCESS) {
162224090Sdougb		fprintf(stderr, "dns_lib_init failed: %d\n", result);
163224090Sdougb		exit(1);
164224090Sdougb	}
165224090Sdougb	result = isc_mem_create(0, 0, &umctx);
166224090Sdougb	if (result != ISC_R_SUCCESS) {
167224090Sdougb		fprintf(stderr, "failed to crate mctx\n");
168224090Sdougb		exit(1);
169224090Sdougb	}
170224090Sdougb
171224090Sdougb	result = dns_client_create(&client, 0);
172224090Sdougb	if (result != ISC_R_SUCCESS) {
173224090Sdougb		fprintf(stderr, "dns_client_create failed: %d\n", result);
174224090Sdougb		exit(1);
175224090Sdougb	}
176224090Sdougb
177224090Sdougb	/* Set the authoritative server */
178224090Sdougb	if (auth_server != NULL) {
179224090Sdougb		memset(&hints, 0, sizeof(hints));
180224090Sdougb		hints.ai_family = AF_UNSPEC;
181224090Sdougb		hints.ai_socktype = SOCK_DGRAM;
182224090Sdougb		hints.ai_protocol = IPPROTO_UDP;
183224090Sdougb		hints.ai_flags = AI_NUMERICHOST;
184224090Sdougb		gai_error = getaddrinfo(auth_server, "53", &hints, &res);
185224090Sdougb		if (gai_error != 0) {
186224090Sdougb			fprintf(stderr, "getaddrinfo failed: %s\n",
187224090Sdougb				gai_strerror(gai_error));
188224090Sdougb			exit(1);
189224090Sdougb		}
190224090Sdougb		INSIST(res->ai_addrlen <= sizeof(sa_auth.type));
191262706Serwin		memmove(&sa_auth.type, res->ai_addr, res->ai_addrlen);
192224090Sdougb		freeaddrinfo(res);
193224090Sdougb		sa_auth.length = res->ai_addrlen;
194224090Sdougb		ISC_LINK_INIT(&sa_auth, link);
195224090Sdougb
196224090Sdougb		ISC_LIST_APPEND(auth_servers, &sa_auth, link);
197224090Sdougb	}
198224090Sdougb
199224090Sdougb	/* Set the recursive server */
200224090Sdougb	if (recursive_server != NULL) {
201224090Sdougb		memset(&hints, 0, sizeof(hints));
202224090Sdougb		hints.ai_family = AF_UNSPEC;
203224090Sdougb		hints.ai_socktype = SOCK_DGRAM;
204224090Sdougb		hints.ai_protocol = IPPROTO_UDP;
205224090Sdougb		hints.ai_flags = AI_NUMERICHOST;
206224090Sdougb		gai_error = getaddrinfo(recursive_server, "53", &hints, &res);
207224090Sdougb		if (gai_error != 0) {
208224090Sdougb			fprintf(stderr, "getaddrinfo failed: %s\n",
209224090Sdougb				gai_strerror(gai_error));
210224090Sdougb			exit(1);
211224090Sdougb		}
212224090Sdougb		INSIST(res->ai_addrlen <= sizeof(sa_recursive.type));
213262706Serwin		memmove(&sa_recursive.type, res->ai_addr, res->ai_addrlen);
214224090Sdougb		freeaddrinfo(res);
215224090Sdougb		sa_recursive.length = res->ai_addrlen;
216224090Sdougb		ISC_LINK_INIT(&sa_recursive, link);
217224090Sdougb		ISC_LIST_INIT(rec_servers);
218224090Sdougb		ISC_LIST_APPEND(rec_servers, &sa_recursive, link);
219224090Sdougb		result = dns_client_setservers(client, dns_rdataclass_in,
220224090Sdougb					       NULL, &rec_servers);
221224090Sdougb		if (result != ISC_R_SUCCESS) {
222224090Sdougb			fprintf(stderr, "set server failed: %d\n", result);
223224090Sdougb			exit(1);
224224090Sdougb		}
225224090Sdougb	}
226224090Sdougb
227224090Sdougb	/* Construct zone name */
228224090Sdougb	zname = NULL;
229224090Sdougb	if (zonenamestr != NULL) {
230224090Sdougb		namelen = strlen(zonenamestr);
231224090Sdougb		isc_buffer_init(&b, zonenamestr, namelen);
232224090Sdougb		isc_buffer_add(&b, namelen);
233224090Sdougb		dns_fixedname_init(&zname0);
234224090Sdougb		zname = dns_fixedname_name(&zname0);
235224090Sdougb		result = dns_name_fromtext(zname, &b, dns_rootname, 0, NULL);
236224090Sdougb		if (result != ISC_R_SUCCESS)
237224090Sdougb			fprintf(stderr, "failed to convert zone name: %d\n",
238224090Sdougb				result);
239224090Sdougb	}
240224090Sdougb
241224090Sdougb	/* Construct prerequisite name (if given) */
242224090Sdougb	if (prereqstr != NULL) {
243224090Sdougb		dns_fixedname_init(&pname0);
244224090Sdougb		pname = dns_fixedname_name(&pname0);
245224090Sdougb		evaluate_prereq(umctx, prereqstr, pname);
246224090Sdougb		ISC_LIST_APPEND(prereqlist, pname, link);
247224090Sdougb		prereqlistp = &prereqlist;
248224090Sdougb	}
249224090Sdougb
250224090Sdougb	/* Construct update name */
251224090Sdougb	ISC_LIST_INIT(updatelist);
252224090Sdougb	dns_fixedname_init(&uname0);
253224090Sdougb	uname = dns_fixedname_name(&uname0);
254224090Sdougb	update_addordelete(umctx, argv[1], isdelete, uname);
255224090Sdougb	ISC_LIST_APPEND(updatelist, uname, link);
256224090Sdougb
257224090Sdougb	/* Set up TSIG/SIG(0) key (if given) */
258224090Sdougb	if (keyfilename != NULL)
259224090Sdougb		setup_tsec(keyfilename, umctx);
260224090Sdougb
261224090Sdougb	/* Perform update */
262224090Sdougb	result = dns_client_update(client,
263224090Sdougb				   default_rdataclass, /* XXX: fixed */
264224090Sdougb				   zname, prereqlistp, &updatelist,
265224090Sdougb				   (auth_server == NULL) ? NULL :
266224090Sdougb				   &auth_servers, tsec, 0);
267224090Sdougb	if (result != ISC_R_SUCCESS) {
268224090Sdougb		fprintf(stderr,
269224090Sdougb			"update failed: %s\n", dns_result_totext(result));
270224090Sdougb	} else
271224090Sdougb		fprintf(stderr, "update succeeded\n");
272224090Sdougb
273224090Sdougb	/* Cleanup */
274224090Sdougb	while ((pname = ISC_LIST_HEAD(prereqlist)) != NULL) {
275224090Sdougb		while ((rdataset = ISC_LIST_HEAD(pname->list)) != NULL) {
276224090Sdougb			ISC_LIST_UNLINK(pname->list, rdataset, link);
277224090Sdougb			dns_rdataset_disassociate(rdataset);
278224090Sdougb			isc_mem_put(umctx, rdataset, sizeof(*rdataset));
279224090Sdougb		}
280224090Sdougb		ISC_LIST_UNLINK(prereqlist, pname, link);
281224090Sdougb	}
282224090Sdougb	while ((uname = ISC_LIST_HEAD(updatelist)) != NULL) {
283224090Sdougb		while ((rdataset = ISC_LIST_HEAD(uname->list)) != NULL) {
284224090Sdougb			ISC_LIST_UNLINK(uname->list, rdataset, link);
285224090Sdougb			dns_rdataset_disassociate(rdataset);
286224090Sdougb			isc_mem_put(umctx, rdataset, sizeof(*rdataset));
287224090Sdougb		}
288224090Sdougb		ISC_LIST_UNLINK(updatelist, uname, link);
289224090Sdougb	}
290224090Sdougb	while ((rdatalist = ISC_LIST_HEAD(usedrdatalists)) != NULL) {
291224090Sdougb		while ((rdata = ISC_LIST_HEAD(rdatalist->rdata)) != NULL) {
292224090Sdougb			ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
293224090Sdougb			isc_mem_put(umctx, rdata, sizeof(*rdata));
294224090Sdougb		}
295224090Sdougb		ISC_LIST_UNLINK(usedrdatalists, rdatalist, link);
296224090Sdougb		isc_mem_put(umctx, rdatalist, sizeof(*rdatalist));
297224090Sdougb	}
298224090Sdougb	while ((buf = ISC_LIST_HEAD(usedbuffers)) != NULL) {
299224090Sdougb		ISC_LIST_UNLINK(usedbuffers, buf, link);
300224090Sdougb		isc_buffer_free(&buf);
301224090Sdougb	}
302224090Sdougb	if (tsec != NULL)
303224090Sdougb		dns_tsec_destroy(&tsec);
304224090Sdougb	isc_mem_destroy(&umctx);
305224090Sdougb	dns_client_destroy(&client);
306224090Sdougb	dns_lib_shutdown();
307224090Sdougb
308254402Serwin	return (0);
309224090Sdougb}
310224090Sdougb
311224090Sdougb/*
312224090Sdougb *  Subroutines borrowed from nsupdate.c
313224090Sdougb */
314224090Sdougb#define MAXWIRE (64 * 1024)
315224090Sdougb#define TTL_MAX 2147483647U	/* Maximum signed 32 bit integer. */
316224090Sdougb
317224090Sdougbstatic char *
318224090Sdougbnsu_strsep(char **stringp, const char *delim) {
319224090Sdougb	char *string = *stringp;
320224090Sdougb	char *s;
321224090Sdougb	const char *d;
322224090Sdougb	char sc, dc;
323224090Sdougb
324224090Sdougb	if (string == NULL)
325224090Sdougb		return (NULL);
326224090Sdougb
327224090Sdougb	for (; *string != '\0'; string++) {
328224090Sdougb		sc = *string;
329224090Sdougb		for (d = delim; (dc = *d) != '\0'; d++) {
330224090Sdougb			if (sc == dc)
331224090Sdougb				break;
332224090Sdougb		}
333224090Sdougb		if (dc == 0)
334224090Sdougb			break;
335224090Sdougb	}
336224090Sdougb
337224090Sdougb	for (s = string; *s != '\0'; s++) {
338224090Sdougb		sc = *s;
339224090Sdougb		for (d = delim; (dc = *d) != '\0'; d++) {
340224090Sdougb			if (sc == dc) {
341224090Sdougb				*s++ = '\0';
342224090Sdougb				*stringp = s;
343224090Sdougb				return (string);
344224090Sdougb			}
345224090Sdougb		}
346224090Sdougb	}
347224090Sdougb	*stringp = NULL;
348224090Sdougb	return (string);
349224090Sdougb}
350224090Sdougb
351224090Sdougbstatic void
352224090Sdougbfatal(const char *format, ...) {
353224090Sdougb	va_list args;
354224090Sdougb
355224090Sdougb	va_start(args, format);
356224090Sdougb	vfprintf(stderr, format, args);
357224090Sdougb	va_end(args);
358224090Sdougb	fprintf(stderr, "\n");
359224090Sdougb	exit(1);
360224090Sdougb}
361224090Sdougb
362224090Sdougbstatic inline void
363224090Sdougbcheck_result(isc_result_t result, const char *msg) {
364224090Sdougb	if (result != ISC_R_SUCCESS)
365224090Sdougb		fatal("%s: %s", msg, isc_result_totext(result));
366224090Sdougb}
367224090Sdougb
368224090Sdougbstatic void
369224090Sdougbparse_name(char **cmdlinep, dns_name_t *name) {
370224090Sdougb	isc_result_t result;
371224090Sdougb	char *word;
372224090Sdougb	isc_buffer_t source;
373224090Sdougb
374224090Sdougb	word = nsu_strsep(cmdlinep, " \t\r\n");
375254402Serwin	if (word == NULL || *word == 0) {
376224090Sdougb		fprintf(stderr, "could not read owner name\n");
377224090Sdougb		exit(1);
378224090Sdougb	}
379224090Sdougb
380224090Sdougb	isc_buffer_init(&source, word, strlen(word));
381224090Sdougb	isc_buffer_add(&source, strlen(word));
382224090Sdougb	result = dns_name_fromtext(name, &source, dns_rootname, 0, NULL);
383224090Sdougb	check_result(result, "dns_name_fromtext");
384224090Sdougb	isc_buffer_invalidate(&source);
385224090Sdougb}
386224090Sdougb
387224090Sdougbstatic void
388224090Sdougbparse_rdata(isc_mem_t *mctx, char **cmdlinep, dns_rdataclass_t rdataclass,
389224090Sdougb	    dns_rdatatype_t rdatatype, dns_rdata_t *rdata)
390224090Sdougb{
391224090Sdougb	char *cmdline = *cmdlinep;
392224090Sdougb	isc_buffer_t source, *buf = NULL, *newbuf = NULL;
393224090Sdougb	isc_region_t r;
394224090Sdougb	isc_lex_t *lex = NULL;
395224090Sdougb	dns_rdatacallbacks_t callbacks;
396224090Sdougb	isc_result_t result;
397224090Sdougb
398224090Sdougb	while (cmdline != NULL && *cmdline != 0 &&
399224090Sdougb	       isspace((unsigned char)*cmdline))
400224090Sdougb		cmdline++;
401224090Sdougb
402224090Sdougb	if (cmdline != NULL && *cmdline != 0) {
403224090Sdougb		dns_rdatacallbacks_init(&callbacks);
404224090Sdougb		result = isc_lex_create(mctx, strlen(cmdline), &lex);
405224090Sdougb		check_result(result, "isc_lex_create");
406224090Sdougb		isc_buffer_init(&source, cmdline, strlen(cmdline));
407224090Sdougb		isc_buffer_add(&source, strlen(cmdline));
408224090Sdougb		result = isc_lex_openbuffer(lex, &source);
409224090Sdougb		check_result(result, "isc_lex_openbuffer");
410224090Sdougb		result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
411224090Sdougb		check_result(result, "isc_buffer_allocate");
412224090Sdougb		result = dns_rdata_fromtext(rdata, rdataclass, rdatatype, lex,
413224090Sdougb					    dns_rootname, 0, mctx, buf,
414224090Sdougb					    &callbacks);
415224090Sdougb		isc_lex_destroy(&lex);
416224090Sdougb		if (result == ISC_R_SUCCESS) {
417224090Sdougb			isc_buffer_usedregion(buf, &r);
418224090Sdougb			result = isc_buffer_allocate(mctx, &newbuf, r.length);
419224090Sdougb			check_result(result, "isc_buffer_allocate");
420224090Sdougb			isc_buffer_putmem(newbuf, r.base, r.length);
421224090Sdougb			isc_buffer_usedregion(newbuf, &r);
422224090Sdougb			dns_rdata_reset(rdata);
423224090Sdougb			dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
424224090Sdougb			isc_buffer_free(&buf);
425224090Sdougb			ISC_LIST_APPEND(usedbuffers, newbuf, link);
426224090Sdougb		} else {
427224090Sdougb			fprintf(stderr, "invalid rdata format: %s\n",
428224090Sdougb				isc_result_totext(result));
429224090Sdougb			isc_buffer_free(&buf);
430224090Sdougb			exit(1);
431224090Sdougb		}
432224090Sdougb	} else {
433224090Sdougb		rdata->flags = DNS_RDATA_UPDATE;
434224090Sdougb	}
435224090Sdougb	*cmdlinep = cmdline;
436224090Sdougb}
437224090Sdougb
438224090Sdougbstatic void
439224090Sdougbupdate_addordelete(isc_mem_t *mctx, char *cmdline, isc_boolean_t isdelete,
440224090Sdougb		   dns_name_t *name)
441224090Sdougb{
442224090Sdougb	isc_result_t result;
443224090Sdougb	isc_uint32_t ttl;
444224090Sdougb	char *word;
445224090Sdougb	dns_rdataclass_t rdataclass;
446224090Sdougb	dns_rdatatype_t rdatatype;
447224090Sdougb	dns_rdata_t *rdata = NULL;
448224090Sdougb	dns_rdatalist_t *rdatalist = NULL;
449224090Sdougb	dns_rdataset_t *rdataset = NULL;
450224090Sdougb	isc_textregion_t region;
451224090Sdougb
452224090Sdougb	/*
453224090Sdougb	 * Read the owner name.
454224090Sdougb	 */
455224090Sdougb	parse_name(&cmdline, name);
456224090Sdougb
457224090Sdougb	rdata = isc_mem_get(mctx, sizeof(*rdata));
458224090Sdougb	if (rdata == NULL) {
459224090Sdougb		fprintf(stderr, "memory allocation for rdata failed\n");
460224090Sdougb		exit(1);
461224090Sdougb	}
462224090Sdougb	dns_rdata_init(rdata);
463224090Sdougb
464224090Sdougb	/*
465224090Sdougb	 * If this is an add, read the TTL and verify that it's in range.
466224090Sdougb	 * If it's a delete, ignore a TTL if present (for compatibility).
467224090Sdougb	 */
468224090Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
469224090Sdougb	if (word == NULL || *word == 0) {
470224090Sdougb		if (!isdelete) {
471224090Sdougb			fprintf(stderr, "could not read owner ttl\n");
472224090Sdougb			exit(1);
473224090Sdougb		}
474224090Sdougb		else {
475224090Sdougb			ttl = 0;
476224090Sdougb			rdataclass = dns_rdataclass_any;
477224090Sdougb			rdatatype = dns_rdatatype_any;
478224090Sdougb			rdata->flags = DNS_RDATA_UPDATE;
479224090Sdougb			goto doneparsing;
480224090Sdougb		}
481224090Sdougb	}
482224090Sdougb	result = isc_parse_uint32(&ttl, word, 10);
483224090Sdougb	if (result != ISC_R_SUCCESS) {
484224090Sdougb		if (isdelete) {
485224090Sdougb			ttl = 0;
486224090Sdougb			goto parseclass;
487224090Sdougb		} else {
488224090Sdougb			fprintf(stderr, "ttl '%s': %s\n", word,
489224090Sdougb				isc_result_totext(result));
490224090Sdougb			exit(1);
491224090Sdougb		}
492224090Sdougb	}
493224090Sdougb
494224090Sdougb	if (isdelete)
495224090Sdougb		ttl = 0;
496224090Sdougb	else if (ttl > TTL_MAX) {
497224090Sdougb		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
498224090Sdougb			word, TTL_MAX);
499224090Sdougb		exit(1);
500224090Sdougb	}
501224090Sdougb
502224090Sdougb	/*
503224090Sdougb	 * Read the class or type.
504224090Sdougb	 */
505224090Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
506224090Sdougb parseclass:
507224090Sdougb	if (word == NULL || *word == 0) {
508224090Sdougb		if (isdelete) {
509224090Sdougb			rdataclass = dns_rdataclass_any;
510224090Sdougb			rdatatype = dns_rdatatype_any;
511224090Sdougb			rdata->flags = DNS_RDATA_UPDATE;
512224090Sdougb		goto doneparsing;
513224090Sdougb		} else {
514224090Sdougb			fprintf(stderr, "could not read class or type\n");
515224090Sdougb			exit(1);
516224090Sdougb		}
517224090Sdougb	}
518224090Sdougb	region.base = word;
519224090Sdougb	region.length = strlen(word);
520224090Sdougb	result = dns_rdataclass_fromtext(&rdataclass, &region);
521224090Sdougb	if (result == ISC_R_SUCCESS) {
522224090Sdougb		/*
523224090Sdougb		 * Now read the type.
524224090Sdougb		 */
525224090Sdougb		word = nsu_strsep(&cmdline, " \t\r\n");
526224090Sdougb		if (word == NULL || *word == 0) {
527224090Sdougb			if (isdelete) {
528224090Sdougb				rdataclass = dns_rdataclass_any;
529224090Sdougb				rdatatype = dns_rdatatype_any;
530224090Sdougb				rdata->flags = DNS_RDATA_UPDATE;
531224090Sdougb				goto doneparsing;
532224090Sdougb			} else {
533224090Sdougb				fprintf(stderr, "could not read type\n");
534224090Sdougb				exit(1);
535224090Sdougb			}
536224090Sdougb		}
537224090Sdougb		region.base = word;
538224090Sdougb		region.length = strlen(word);
539224090Sdougb		result = dns_rdatatype_fromtext(&rdatatype, &region);
540224090Sdougb		if (result != ISC_R_SUCCESS) {
541224090Sdougb			fprintf(stderr, "'%s' is not a valid type: %s\n",
542224090Sdougb				word, isc_result_totext(result));
543224090Sdougb			exit(1);
544224090Sdougb		}
545224090Sdougb	} else {
546224090Sdougb		rdataclass = default_rdataclass;
547224090Sdougb		result = dns_rdatatype_fromtext(&rdatatype, &region);
548224090Sdougb		if (result != ISC_R_SUCCESS) {
549224090Sdougb			fprintf(stderr, "'%s' is not a valid class or type: "
550224090Sdougb				"%s\n", word, isc_result_totext(result));
551224090Sdougb			exit(1);
552224090Sdougb		}
553224090Sdougb	}
554224090Sdougb
555224090Sdougb	parse_rdata(mctx, &cmdline, rdataclass, rdatatype, rdata);
556224090Sdougb
557224090Sdougb	if (isdelete) {
558224090Sdougb		if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
559224090Sdougb			rdataclass = dns_rdataclass_any;
560224090Sdougb		else
561224090Sdougb			rdataclass = dns_rdataclass_none;
562224090Sdougb	} else {
563224090Sdougb		if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
564224090Sdougb			fprintf(stderr, "could not read rdata\n");
565224090Sdougb			exit(1);
566224090Sdougb		}
567224090Sdougb	}
568224090Sdougb
569224090Sdougb doneparsing:
570224090Sdougb
571224090Sdougb	rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
572224090Sdougb	if (rdatalist == NULL) {
573224090Sdougb		fprintf(stderr, "memory allocation for rdatalist failed\n");
574224090Sdougb		exit(1);
575224090Sdougb	}
576224090Sdougb	dns_rdatalist_init(rdatalist);
577224090Sdougb	rdatalist->type = rdatatype;
578224090Sdougb	rdatalist->rdclass = rdataclass;
579224090Sdougb	rdatalist->covers = rdatatype;
580224090Sdougb	rdatalist->ttl = (dns_ttl_t)ttl;
581224090Sdougb	ISC_LIST_INIT(rdatalist->rdata);
582224090Sdougb	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
583224090Sdougb	ISC_LIST_APPEND(usedrdatalists, rdatalist, link);
584224090Sdougb
585224090Sdougb	rdataset = isc_mem_get(mctx, sizeof(*rdataset));
586224090Sdougb	if (rdataset == NULL) {
587224090Sdougb		fprintf(stderr, "memory allocation for rdataset failed\n");
588224090Sdougb		exit(1);
589224090Sdougb	}
590224090Sdougb	dns_rdataset_init(rdataset);
591224090Sdougb	dns_rdatalist_tordataset(rdatalist, rdataset);
592224090Sdougb	ISC_LIST_INIT(name->list);
593224090Sdougb	ISC_LIST_APPEND(name->list, rdataset, link);
594224090Sdougb}
595224090Sdougb
596224090Sdougbstatic void
597224090Sdougbmake_prereq(isc_mem_t *mctx, char *cmdline, isc_boolean_t ispositive,
598224090Sdougb	    isc_boolean_t isrrset, dns_name_t *name)
599224090Sdougb{
600224090Sdougb	isc_result_t result;
601224090Sdougb	char *word;
602224090Sdougb	isc_textregion_t region;
603224090Sdougb	dns_rdataset_t *rdataset = NULL;
604224090Sdougb	dns_rdatalist_t *rdatalist = NULL;
605224090Sdougb	dns_rdataclass_t rdataclass;
606224090Sdougb	dns_rdatatype_t rdatatype;
607224090Sdougb	dns_rdata_t *rdata = NULL;
608224090Sdougb
609224090Sdougb	/*
610224090Sdougb	 * Read the owner name
611224090Sdougb	 */
612224090Sdougb	parse_name(&cmdline, name);
613224090Sdougb
614224090Sdougb	/*
615224090Sdougb	 * If this is an rrset prereq, read the class or type.
616224090Sdougb	 */
617224090Sdougb	if (isrrset) {
618224090Sdougb		word = nsu_strsep(&cmdline, " \t\r\n");
619224090Sdougb		if (word == NULL || *word == 0) {
620224090Sdougb			fprintf(stderr, "could not read class or type\n");
621224090Sdougb			exit(1);
622224090Sdougb		}
623224090Sdougb		region.base = word;
624224090Sdougb		region.length = strlen(word);
625224090Sdougb		result = dns_rdataclass_fromtext(&rdataclass, &region);
626224090Sdougb		if (result == ISC_R_SUCCESS) {
627224090Sdougb			/*
628224090Sdougb			 * Now read the type.
629224090Sdougb			 */
630224090Sdougb			word = nsu_strsep(&cmdline, " \t\r\n");
631224090Sdougb			if (word == NULL || *word == 0) {
632224090Sdougb				fprintf(stderr, "could not read type\n");
633224090Sdougb				exit(1);
634224090Sdougb			}
635224090Sdougb			region.base = word;
636224090Sdougb			region.length = strlen(word);
637224090Sdougb			result = dns_rdatatype_fromtext(&rdatatype, &region);
638224090Sdougb			if (result != ISC_R_SUCCESS) {
639224090Sdougb				fprintf(stderr, "invalid type: %s\n", word);
640224090Sdougb				exit(1);
641224090Sdougb			}
642224090Sdougb		} else {
643224090Sdougb			rdataclass = default_rdataclass;
644224090Sdougb			result = dns_rdatatype_fromtext(&rdatatype, &region);
645224090Sdougb			if (result != ISC_R_SUCCESS) {
646224090Sdougb				fprintf(stderr, "invalid type: %s\n", word);
647224090Sdougb				exit(1);
648224090Sdougb			}
649224090Sdougb		}
650224090Sdougb	} else
651224090Sdougb		rdatatype = dns_rdatatype_any;
652224090Sdougb
653224090Sdougb	rdata = isc_mem_get(mctx, sizeof(*rdata));
654224090Sdougb	if (rdata == NULL) {
655224090Sdougb		fprintf(stderr, "memory allocation for rdata failed\n");
656224090Sdougb		exit(1);
657224090Sdougb	}
658224090Sdougb	dns_rdata_init(rdata);
659224090Sdougb
660224090Sdougb	if (isrrset && ispositive)
661224090Sdougb		parse_rdata(mctx, &cmdline, rdataclass, rdatatype, rdata);
662224090Sdougb	else
663224090Sdougb		rdata->flags = DNS_RDATA_UPDATE;
664224090Sdougb
665224090Sdougb	rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
666224090Sdougb	if (rdatalist == NULL) {
667224090Sdougb		fprintf(stderr, "memory allocation for rdatalist failed\n");
668224090Sdougb		exit(1);
669224090Sdougb	}
670224090Sdougb	dns_rdatalist_init(rdatalist);
671224090Sdougb	rdatalist->type = rdatatype;
672224090Sdougb	if (ispositive) {
673224090Sdougb		if (isrrset && rdata->data != NULL)
674224090Sdougb			rdatalist->rdclass = rdataclass;
675224090Sdougb		else
676224090Sdougb			rdatalist->rdclass = dns_rdataclass_any;
677224090Sdougb	} else
678224090Sdougb		rdatalist->rdclass = dns_rdataclass_none;
679224090Sdougb	rdatalist->covers = 0;
680224090Sdougb	rdatalist->ttl = 0;
681224090Sdougb	rdata->rdclass = rdatalist->rdclass;
682224090Sdougb	rdata->type = rdatatype;
683224090Sdougb	ISC_LIST_INIT(rdatalist->rdata);
684224090Sdougb	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
685224090Sdougb	ISC_LIST_APPEND(usedrdatalists, rdatalist, link);
686224090Sdougb
687224090Sdougb	rdataset = isc_mem_get(mctx, sizeof(*rdataset));
688224090Sdougb	if (rdataset == NULL) {
689224090Sdougb		fprintf(stderr, "memory allocation for rdataset failed\n");
690224090Sdougb		exit(1);
691224090Sdougb	}
692224090Sdougb	dns_rdataset_init(rdataset);
693224090Sdougb	dns_rdatalist_tordataset(rdatalist, rdataset);
694224090Sdougb	ISC_LIST_INIT(name->list);
695224090Sdougb	ISC_LIST_APPEND(name->list, rdataset, link);
696224090Sdougb}
697224090Sdougb
698224090Sdougbstatic void
699224090Sdougbevaluate_prereq(isc_mem_t *mctx, char *cmdline, dns_name_t *name) {
700224090Sdougb	char *word;
701224090Sdougb	isc_boolean_t ispositive, isrrset;
702224090Sdougb
703224090Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
704224090Sdougb	if (word == NULL || *word == 0) {
705224090Sdougb		fprintf(stderr, "could not read operation code\n");
706224090Sdougb		exit(1);
707224090Sdougb	}
708224090Sdougb	if (strcasecmp(word, "nxdomain") == 0) {
709224090Sdougb		ispositive = ISC_FALSE;
710224090Sdougb		isrrset = ISC_FALSE;
711224090Sdougb	} else if (strcasecmp(word, "yxdomain") == 0) {
712224090Sdougb		ispositive = ISC_TRUE;
713224090Sdougb		isrrset = ISC_FALSE;
714224090Sdougb	} else if (strcasecmp(word, "nxrrset") == 0) {
715224090Sdougb		ispositive = ISC_FALSE;
716224090Sdougb		isrrset = ISC_TRUE;
717224090Sdougb	} else if (strcasecmp(word, "yxrrset") == 0) {
718224090Sdougb		ispositive = ISC_TRUE;
719224090Sdougb		isrrset = ISC_TRUE;
720224090Sdougb	} else {
721224090Sdougb		fprintf(stderr, "incorrect operation code: %s\n", word);
722224090Sdougb		exit(1);
723224090Sdougb	}
724224090Sdougb
725224090Sdougb	make_prereq(mctx, cmdline, ispositive, isrrset, name);
726224090Sdougb}
727224090Sdougb
728224090Sdougbstatic void
729224090Sdougbsetup_tsec(char *keyfile, isc_mem_t *mctx) {
730224090Sdougb	dst_key_t *dstkey = NULL;
731224090Sdougb	isc_result_t result;
732224090Sdougb	dns_tsectype_t tsectype;
733224090Sdougb
734224090Sdougb	result = dst_key_fromnamedfile(keyfile, NULL,
735224090Sdougb				       DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
736224090Sdougb				       &dstkey);
737224090Sdougb	if (result != ISC_R_SUCCESS) {
738224090Sdougb		fprintf(stderr, "could not read key from %s: %s\n",
739224090Sdougb			keyfile, isc_result_totext(result));
740224090Sdougb		exit(1);
741224090Sdougb	}
742224090Sdougb
743224090Sdougb	if (dst_key_alg(dstkey) == DST_ALG_HMACMD5)
744224090Sdougb		tsectype = dns_tsectype_tsig;
745224090Sdougb	else
746224090Sdougb		tsectype = dns_tsectype_sig0;
747224090Sdougb
748224090Sdougb	result = dns_tsec_create(mctx, tsectype, dstkey, &tsec);
749224090Sdougb	dst_key_free(&dstkey);
750224090Sdougb	if (result != ISC_R_SUCCESS) {
751224090Sdougb		fprintf(stderr, "could not create tsec: %s\n",
752224090Sdougb			isc_result_totext(result));
753224090Sdougb		exit(1);
754224090Sdougb	}
755224090Sdougb}
756