nsupdate.c revision 193149
1135446Strhodes/*
2193149Sdougb * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18193149Sdougb/* $Id: nsupdate.c,v 1.163.48.3 2009/04/30 07:12:49 marka Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <ctype.h>
25135446Strhodes#include <errno.h>
26135446Strhodes#include <limits.h>
27135446Strhodes#include <stdlib.h>
28135446Strhodes#include <unistd.h>
29135446Strhodes
30135446Strhodes#include <isc/app.h>
31135446Strhodes#include <isc/base64.h>
32135446Strhodes#include <isc/buffer.h>
33135446Strhodes#include <isc/commandline.h>
34135446Strhodes#include <isc/entropy.h>
35135446Strhodes#include <isc/event.h>
36135446Strhodes#include <isc/hash.h>
37135446Strhodes#include <isc/lex.h>
38193149Sdougb#include <isc/log.h>
39135446Strhodes#include <isc/mem.h>
40135446Strhodes#include <isc/parseint.h>
41193149Sdougb#include <isc/random.h>
42135446Strhodes#include <isc/region.h>
43135446Strhodes#include <isc/sockaddr.h>
44135446Strhodes#include <isc/socket.h>
45135446Strhodes#include <isc/stdio.h>
46135446Strhodes#include <isc/string.h>
47135446Strhodes#include <isc/task.h>
48135446Strhodes#include <isc/timer.h>
49135446Strhodes#include <isc/types.h>
50135446Strhodes#include <isc/util.h>
51135446Strhodes
52135446Strhodes#include <dns/callbacks.h>
53135446Strhodes#include <dns/dispatch.h>
54135446Strhodes#include <dns/dnssec.h>
55135446Strhodes#include <dns/events.h>
56135446Strhodes#include <dns/fixedname.h>
57193149Sdougb#include <dns/log.h>
58135446Strhodes#include <dns/masterdump.h>
59135446Strhodes#include <dns/message.h>
60135446Strhodes#include <dns/name.h>
61135446Strhodes#include <dns/rcode.h>
62135446Strhodes#include <dns/rdata.h>
63135446Strhodes#include <dns/rdataclass.h>
64135446Strhodes#include <dns/rdatalist.h>
65135446Strhodes#include <dns/rdataset.h>
66135446Strhodes#include <dns/rdatastruct.h>
67135446Strhodes#include <dns/rdatatype.h>
68135446Strhodes#include <dns/request.h>
69135446Strhodes#include <dns/result.h>
70193149Sdougb#include <dns/tkey.h>
71135446Strhodes#include <dns/tsig.h>
72135446Strhodes
73135446Strhodes#include <dst/dst.h>
74135446Strhodes
75135446Strhodes#include <lwres/lwres.h>
76135446Strhodes#include <lwres/net.h>
77135446Strhodes
78193149Sdougb#ifdef GSSAPI
79193149Sdougb#include <dst/gssapi.h>
80193149Sdougb#endif
81135446Strhodes#include <bind9/getaddresses.h>
82135446Strhodes
83193149Sdougb
84135446Strhodes#ifdef HAVE_ADDRINFO
85135446Strhodes#ifdef HAVE_GETADDRINFO
86135446Strhodes#ifdef HAVE_GAISTRERROR
87135446Strhodes#define USE_GETADDRINFO
88135446Strhodes#endif
89135446Strhodes#endif
90135446Strhodes#endif
91135446Strhodes
92135446Strhodes#ifndef USE_GETADDRINFO
93135446Strhodes#ifndef ISC_PLATFORM_NONSTDHERRNO
94135446Strhodesextern int h_errno;
95135446Strhodes#endif
96135446Strhodes#endif
97135446Strhodes
98135446Strhodes#define MAXCMD (4 * 1024)
99135446Strhodes#define MAXWIRE (64 * 1024)
100135446Strhodes#define PACKETSIZE ((64 * 1024) - 1)
101135446Strhodes#define INITTEXT (2 * 1024)
102135446Strhodes#define MAXTEXT (128 * 1024)
103135446Strhodes#define FIND_TIMEOUT 5
104135446Strhodes#define TTL_MAX 2147483647U	/* Maximum signed 32 bit integer. */
105135446Strhodes
106135446Strhodes#define DNSDEFAULTPORT 53
107135446Strhodes
108135446Strhodes#ifndef RESOLV_CONF
109135446Strhodes#define RESOLV_CONF "/etc/resolv.conf"
110135446Strhodes#endif
111135446Strhodes
112135446Strhodesstatic isc_boolean_t debugging = ISC_FALSE, ddebugging = ISC_FALSE;
113135446Strhodesstatic isc_boolean_t memdebugging = ISC_FALSE;
114135446Strhodesstatic isc_boolean_t have_ipv4 = ISC_FALSE;
115135446Strhodesstatic isc_boolean_t have_ipv6 = ISC_FALSE;
116135446Strhodesstatic isc_boolean_t is_dst_up = ISC_FALSE;
117135446Strhodesstatic isc_boolean_t usevc = ISC_FALSE;
118193149Sdougbstatic isc_boolean_t usegsstsig = ISC_FALSE;
119193149Sdougbstatic isc_boolean_t use_win2k_gsstsig = ISC_FALSE;
120193149Sdougbstatic isc_boolean_t tried_other_gsstsig = ISC_FALSE;
121135446Strhodesstatic isc_taskmgr_t *taskmgr = NULL;
122135446Strhodesstatic isc_task_t *global_task = NULL;
123135446Strhodesstatic isc_event_t *global_event = NULL;
124193149Sdougbstatic isc_log_t *lctx = NULL;
125135446Strhodesstatic isc_mem_t *mctx = NULL;
126135446Strhodesstatic dns_dispatchmgr_t *dispatchmgr = NULL;
127135446Strhodesstatic dns_requestmgr_t *requestmgr = NULL;
128135446Strhodesstatic isc_socketmgr_t *socketmgr = NULL;
129135446Strhodesstatic isc_timermgr_t *timermgr = NULL;
130135446Strhodesstatic dns_dispatch_t *dispatchv4 = NULL;
131135446Strhodesstatic dns_dispatch_t *dispatchv6 = NULL;
132135446Strhodesstatic dns_message_t *updatemsg = NULL;
133135446Strhodesstatic dns_fixedname_t fuserzone;
134135446Strhodesstatic dns_name_t *userzone = NULL;
135193149Sdougbstatic dns_name_t *zonename = NULL;
136193149Sdougbstatic dns_name_t tmpzonename;
137193149Sdougbstatic dns_name_t restart_master;
138193149Sdougbstatic dns_tsig_keyring_t *gssring = NULL;
139135446Strhodesstatic dns_tsigkey_t *tsigkey = NULL;
140135446Strhodesstatic dst_key_t *sig0key;
141135446Strhodesstatic lwres_context_t *lwctx = NULL;
142135446Strhodesstatic lwres_conf_t *lwconf;
143135446Strhodesstatic isc_sockaddr_t *servers;
144135446Strhodesstatic int ns_inuse = 0;
145135446Strhodesstatic int ns_total = 0;
146135446Strhodesstatic isc_sockaddr_t *userserver = NULL;
147135446Strhodesstatic isc_sockaddr_t *localaddr = NULL;
148193149Sdougbstatic isc_sockaddr_t *serveraddr = NULL;
149193149Sdougbstatic isc_sockaddr_t tempaddr;
150135446Strhodesstatic char *keystr = NULL, *keyfile = NULL;
151193149Sdougbstatic isc_entropy_t *entropy = NULL;
152135446Strhodesstatic isc_boolean_t shuttingdown = ISC_FALSE;
153135446Strhodesstatic FILE *input;
154135446Strhodesstatic isc_boolean_t interactive = ISC_TRUE;
155135446Strhodesstatic isc_boolean_t seenerror = ISC_FALSE;
156135446Strhodesstatic const dns_master_style_t *style;
157135446Strhodesstatic int requests = 0;
158193149Sdougbstatic unsigned int logdebuglevel = 0;
159135446Strhodesstatic unsigned int timeout = 300;
160135446Strhodesstatic unsigned int udp_timeout = 3;
161135446Strhodesstatic unsigned int udp_retries = 3;
162135446Strhodesstatic dns_rdataclass_t defaultclass = dns_rdataclass_in;
163135446Strhodesstatic dns_rdataclass_t zoneclass = dns_rdataclass_none;
164135446Strhodesstatic dns_message_t *answer = NULL;
165193149Sdougbstatic isc_uint32_t default_ttl = 0;
166193149Sdougbstatic isc_boolean_t default_ttl_set = ISC_FALSE;
167135446Strhodes
168135446Strhodestypedef struct nsu_requestinfo {
169135446Strhodes	dns_message_t *msg;
170135446Strhodes	isc_sockaddr_t *addr;
171135446Strhodes} nsu_requestinfo_t;
172135446Strhodes
173135446Strhodesstatic void
174135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
175135446Strhodes	    dns_message_t *msg, dns_request_t **request);
176135446Strhodesstatic void
177135446Strhodesfatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
178135446Strhodes
179135446Strhodesstatic void
180135446Strhodesdebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
181135446Strhodes
182135446Strhodesstatic void
183135446Strhodesddebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
184135446Strhodes
185193149Sdougb#ifdef GSSAPI
186193149Sdougbstatic dns_fixedname_t fkname;
187193149Sdougbstatic isc_sockaddr_t *kserver = NULL;
188193149Sdougbstatic char servicename[DNS_NAME_FORMATSIZE];
189193149Sdougbstatic dns_name_t *keyname;
190193149Sdougbtypedef struct nsu_gssinfo {
191193149Sdougb	dns_message_t *msg;
192193149Sdougb	isc_sockaddr_t *addr;
193193149Sdougb	gss_ctx_id_t context;
194193149Sdougb} nsu_gssinfo_t;
195193149Sdougb
196170222Sdougbstatic void
197193149Sdougbstart_gssrequest(dns_name_t *master);
198193149Sdougbstatic void
199193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
200193149Sdougb		dns_message_t *msg, dns_request_t **request,
201193149Sdougb		gss_ctx_id_t context);
202193149Sdougbstatic void
203193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event);
204193149Sdougb#endif /* GSSAPI */
205193149Sdougb
206193149Sdougbstatic void
207170222Sdougberror(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
208170222Sdougb
209135446Strhodes#define STATUS_MORE	(isc_uint16_t)0
210135446Strhodes#define STATUS_SEND	(isc_uint16_t)1
211135446Strhodes#define STATUS_QUIT	(isc_uint16_t)2
212135446Strhodes#define STATUS_SYNTAX	(isc_uint16_t)3
213135446Strhodes
214193149Sdougbtypedef struct entropysource entropysource_t;
215193149Sdougb
216193149Sdougbstruct entropysource {
217193149Sdougb	isc_entropysource_t *source;
218193149Sdougb	isc_mem_t *mctx;
219193149Sdougb	ISC_LINK(entropysource_t) link;
220193149Sdougb};
221193149Sdougb
222193149Sdougbstatic ISC_LIST(entropysource_t) sources;
223193149Sdougb
224193149Sdougbstatic void
225193149Sdougbsetup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx)
226193149Sdougb{
227193149Sdougb	isc_result_t result;
228193149Sdougb	isc_entropysource_t *source = NULL;
229193149Sdougb	entropysource_t *elt;
230193149Sdougb	int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
231193149Sdougb
232193149Sdougb	REQUIRE(ectx != NULL);
233193149Sdougb
234193149Sdougb	if (*ectx == NULL) {
235193149Sdougb		result = isc_entropy_create(mctx, ectx);
236193149Sdougb		if (result != ISC_R_SUCCESS)
237193149Sdougb			fatal("could not create entropy object");
238193149Sdougb		ISC_LIST_INIT(sources);
239193149Sdougb	}
240193149Sdougb
241193149Sdougb	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
242193149Sdougb		usekeyboard = ISC_ENTROPY_KEYBOARDYES;
243193149Sdougb		randomfile = NULL;
244193149Sdougb	}
245193149Sdougb
246193149Sdougb	result = isc_entropy_usebestsource(*ectx, &source, randomfile,
247193149Sdougb					   usekeyboard);
248193149Sdougb
249193149Sdougb	if (result != ISC_R_SUCCESS)
250193149Sdougb		fatal("could not initialize entropy source: %s",
251193149Sdougb		      isc_result_totext(result));
252193149Sdougb
253193149Sdougb	if (source != NULL) {
254193149Sdougb		elt = isc_mem_get(mctx, sizeof(*elt));
255193149Sdougb		if (elt == NULL)
256193149Sdougb			fatal("out of memory");
257193149Sdougb		elt->source = source;
258193149Sdougb		elt->mctx = mctx;
259193149Sdougb		ISC_LINK_INIT(elt, link);
260193149Sdougb		ISC_LIST_APPEND(sources, elt, link);
261193149Sdougb	}
262193149Sdougb}
263193149Sdougb
264193149Sdougbstatic void
265193149Sdougbcleanup_entropy(isc_entropy_t **ectx) {
266193149Sdougb	entropysource_t *source;
267193149Sdougb	while (!ISC_LIST_EMPTY(sources)) {
268193149Sdougb		source = ISC_LIST_HEAD(sources);
269193149Sdougb		ISC_LIST_UNLINK(sources, source, link);
270193149Sdougb		isc_entropy_destroysource(&source->source);
271193149Sdougb		isc_mem_put(source->mctx, source, sizeof(*source));
272193149Sdougb	}
273193149Sdougb	isc_entropy_detach(ectx);
274193149Sdougb}
275193149Sdougb
276193149Sdougb
277135446Strhodesstatic dns_rdataclass_t
278135446Strhodesgetzoneclass(void) {
279135446Strhodes	if (zoneclass == dns_rdataclass_none)
280135446Strhodes		zoneclass = defaultclass;
281135446Strhodes	return (zoneclass);
282135446Strhodes}
283135446Strhodes
284135446Strhodesstatic isc_boolean_t
285135446Strhodessetzoneclass(dns_rdataclass_t rdclass) {
286135446Strhodes	if (zoneclass == dns_rdataclass_none ||
287135446Strhodes	    rdclass == dns_rdataclass_none)
288135446Strhodes		zoneclass = rdclass;
289135446Strhodes	if (zoneclass != rdclass)
290135446Strhodes		return (ISC_FALSE);
291135446Strhodes	return (ISC_TRUE);
292135446Strhodes}
293135446Strhodes
294135446Strhodesstatic void
295135446Strhodesfatal(const char *format, ...) {
296135446Strhodes	va_list args;
297135446Strhodes
298135446Strhodes	va_start(args, format);
299135446Strhodes	vfprintf(stderr, format, args);
300135446Strhodes	va_end(args);
301135446Strhodes	fprintf(stderr, "\n");
302135446Strhodes	exit(1);
303135446Strhodes}
304135446Strhodes
305135446Strhodesstatic void
306170222Sdougberror(const char *format, ...) {
307170222Sdougb	va_list args;
308170222Sdougb
309170222Sdougb	va_start(args, format);
310170222Sdougb	vfprintf(stderr, format, args);
311170222Sdougb	va_end(args);
312170222Sdougb	fprintf(stderr, "\n");
313170222Sdougb}
314170222Sdougb
315170222Sdougbstatic void
316135446Strhodesdebug(const char *format, ...) {
317135446Strhodes	va_list args;
318135446Strhodes
319135446Strhodes	if (debugging) {
320135446Strhodes		va_start(args, format);
321135446Strhodes		vfprintf(stderr, format, args);
322135446Strhodes		va_end(args);
323135446Strhodes		fprintf(stderr, "\n");
324135446Strhodes	}
325135446Strhodes}
326135446Strhodes
327135446Strhodesstatic void
328135446Strhodesddebug(const char *format, ...) {
329135446Strhodes	va_list args;
330135446Strhodes
331135446Strhodes	if (ddebugging) {
332135446Strhodes		va_start(args, format);
333135446Strhodes		vfprintf(stderr, format, args);
334135446Strhodes		va_end(args);
335135446Strhodes		fprintf(stderr, "\n");
336135446Strhodes	}
337135446Strhodes}
338135446Strhodes
339135446Strhodesstatic inline void
340135446Strhodescheck_result(isc_result_t result, const char *msg) {
341135446Strhodes	if (result != ISC_R_SUCCESS)
342135446Strhodes		fatal("%s: %s", msg, isc_result_totext(result));
343135446Strhodes}
344135446Strhodes
345135446Strhodesstatic void *
346135446Strhodesmem_alloc(void *arg, size_t size) {
347135446Strhodes	return (isc_mem_get(arg, size));
348135446Strhodes}
349135446Strhodes
350135446Strhodesstatic void
351135446Strhodesmem_free(void *arg, void *mem, size_t size) {
352135446Strhodes	isc_mem_put(arg, mem, size);
353135446Strhodes}
354135446Strhodes
355135446Strhodesstatic char *
356135446Strhodesnsu_strsep(char **stringp, const char *delim) {
357135446Strhodes	char *string = *stringp;
358135446Strhodes	char *s;
359135446Strhodes	const char *d;
360135446Strhodes	char sc, dc;
361135446Strhodes
362135446Strhodes	if (string == NULL)
363135446Strhodes		return (NULL);
364135446Strhodes
365135446Strhodes	for (; *string != '\0'; string++) {
366135446Strhodes		sc = *string;
367135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
368135446Strhodes			if (sc == dc)
369135446Strhodes				break;
370135446Strhodes		}
371135446Strhodes		if (dc == 0)
372135446Strhodes			break;
373135446Strhodes	}
374135446Strhodes
375135446Strhodes	for (s = string; *s != '\0'; s++) {
376135446Strhodes		sc = *s;
377135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
378135446Strhodes			if (sc == dc) {
379135446Strhodes				*s++ = '\0';
380135446Strhodes				*stringp = s;
381135446Strhodes				return (string);
382135446Strhodes			}
383135446Strhodes		}
384135446Strhodes	}
385135446Strhodes	*stringp = NULL;
386135446Strhodes	return (string);
387135446Strhodes}
388135446Strhodes
389135446Strhodesstatic void
390135446Strhodesreset_system(void) {
391135446Strhodes	isc_result_t result;
392135446Strhodes
393135446Strhodes	ddebug("reset_system()");
394135446Strhodes	/* If the update message is still around, destroy it */
395135446Strhodes	if (updatemsg != NULL)
396135446Strhodes		dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER);
397135446Strhodes	else {
398135446Strhodes		result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
399135446Strhodes					    &updatemsg);
400135446Strhodes		check_result(result, "dns_message_create");
401135446Strhodes	}
402135446Strhodes	updatemsg->opcode = dns_opcode_update;
403193149Sdougb	if (usegsstsig) {
404193149Sdougb		if (tsigkey != NULL)
405193149Sdougb			dns_tsigkey_detach(&tsigkey);
406193149Sdougb		if (gssring != NULL)
407193149Sdougb			dns_tsigkeyring_destroy(&gssring);
408193149Sdougb		tried_other_gsstsig = ISC_FALSE;
409193149Sdougb	}
410135446Strhodes}
411135446Strhodes
412170222Sdougbstatic isc_uint16_t
413170222Sdougbparse_hmac(dns_name_t **hmac, const char *hmacstr, size_t len) {
414170222Sdougb	isc_uint16_t digestbits = 0;
415170222Sdougb	isc_result_t result;
416170222Sdougb	char buf[20];
417170222Sdougb
418170222Sdougb	REQUIRE(hmac != NULL && *hmac == NULL);
419170222Sdougb	REQUIRE(hmacstr != NULL);
420170222Sdougb
421170222Sdougb	if (len >= sizeof(buf))
422170222Sdougb		fatal("unknown key type '%.*s'", (int)(len), hmacstr);
423170222Sdougb
424170222Sdougb	strncpy(buf, hmacstr, len);
425170222Sdougb	buf[len] = 0;
426186462Sdougb
427170222Sdougb	if (strcasecmp(buf, "hmac-md5") == 0) {
428170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
429170222Sdougb	} else if (strncasecmp(buf, "hmac-md5-", 9) == 0) {
430170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
431170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[9], 10);
432170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 128)
433170222Sdougb			fatal("digest-bits out of range [0..128]");
434170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
435170222Sdougb	} else if (strcasecmp(buf, "hmac-sha1") == 0) {
436170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
437170222Sdougb	} else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) {
438170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
439170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[10], 10);
440170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 160)
441170222Sdougb			fatal("digest-bits out of range [0..160]");
442170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
443170222Sdougb	} else if (strcasecmp(buf, "hmac-sha224") == 0) {
444170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
445170222Sdougb	} else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) {
446170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
447170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
448170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 224)
449170222Sdougb			fatal("digest-bits out of range [0..224]");
450170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
451170222Sdougb	} else if (strcasecmp(buf, "hmac-sha256") == 0) {
452170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
453170222Sdougb	} else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) {
454170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
455170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
456170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 256)
457170222Sdougb			fatal("digest-bits out of range [0..256]");
458170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
459170222Sdougb	} else if (strcasecmp(buf, "hmac-sha384") == 0) {
460170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
461170222Sdougb	} else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) {
462170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
463170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
464170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 384)
465170222Sdougb			fatal("digest-bits out of range [0..384]");
466170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
467170222Sdougb	} else if (strcasecmp(buf, "hmac-sha512") == 0) {
468170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
469170222Sdougb	} else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) {
470170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
471170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
472170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 512)
473170222Sdougb			fatal("digest-bits out of range [0..512]");
474170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
475170222Sdougb	} else
476170222Sdougb		fatal("unknown key type '%s'", buf);
477170222Sdougb	return (digestbits);
478170222Sdougb}
479170222Sdougb
480135446Strhodesstatic void
481135446Strhodessetup_keystr(void) {
482135446Strhodes	unsigned char *secret = NULL;
483135446Strhodes	int secretlen;
484135446Strhodes	isc_buffer_t secretbuf;
485135446Strhodes	isc_result_t result;
486135446Strhodes	isc_buffer_t keynamesrc;
487135446Strhodes	char *secretstr;
488170222Sdougb	char *s, *n;
489135446Strhodes	dns_fixedname_t fkeyname;
490135446Strhodes	dns_name_t *keyname;
491170222Sdougb	char *name;
492170222Sdougb	dns_name_t *hmacname = NULL;
493170222Sdougb	isc_uint16_t digestbits = 0;
494135446Strhodes
495135446Strhodes	dns_fixedname_init(&fkeyname);
496135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
497135446Strhodes
498135446Strhodes	debug("Creating key...");
499135446Strhodes
500135446Strhodes	s = strchr(keystr, ':');
501170222Sdougb	if (s == NULL || s == keystr || s[1] == 0)
502170222Sdougb		fatal("key option must specify [hmac:]keyname:secret");
503135446Strhodes	secretstr = s + 1;
504170222Sdougb	n = strchr(secretstr, ':');
505170222Sdougb	if (n != NULL) {
506170222Sdougb		if (n == secretstr || n[1] == 0)
507170222Sdougb			fatal("key option must specify [hmac:]keyname:secret");
508170222Sdougb		name = secretstr;
509170222Sdougb		secretstr = n + 1;
510170222Sdougb		digestbits = parse_hmac(&hmacname, keystr, s - keystr);
511170222Sdougb	} else {
512170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
513170222Sdougb		name = keystr;
514170222Sdougb		n = s;
515170222Sdougb	}
516135446Strhodes
517170222Sdougb	isc_buffer_init(&keynamesrc, name, n - name);
518170222Sdougb	isc_buffer_add(&keynamesrc, n - name);
519135446Strhodes
520135446Strhodes	debug("namefromtext");
521135446Strhodes	result = dns_name_fromtext(keyname, &keynamesrc, dns_rootname,
522135446Strhodes				   ISC_FALSE, NULL);
523135446Strhodes	check_result(result, "dns_name_fromtext");
524135446Strhodes
525135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
526135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
527135446Strhodes	if (secret == NULL)
528135446Strhodes		fatal("out of memory");
529135446Strhodes
530135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
531135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
532135446Strhodes	if (result != ISC_R_SUCCESS) {
533135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
534135446Strhodes			keystr, isc_result_totext(result));
535135446Strhodes		goto failure;
536135446Strhodes	}
537135446Strhodes
538135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
539135446Strhodes
540135446Strhodes	debug("keycreate");
541170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
542170222Sdougb				    ISC_TRUE, NULL, 0, 0, mctx, NULL, &tsigkey);
543135446Strhodes	if (result != ISC_R_SUCCESS)
544135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
545135446Strhodes			keystr, dns_result_totext(result));
546170222Sdougb	else
547170222Sdougb		dst_key_setbits(tsigkey->key, digestbits);
548135446Strhodes failure:
549135446Strhodes	if (secret != NULL)
550135446Strhodes		isc_mem_free(mctx, secret);
551135446Strhodes}
552135446Strhodes
553135446Strhodesstatic void
554135446Strhodessetup_keyfile(void) {
555135446Strhodes	dst_key_t *dstkey = NULL;
556135446Strhodes	isc_result_t result;
557170222Sdougb	dns_name_t *hmacname = NULL;
558135446Strhodes
559135446Strhodes	debug("Creating key...");
560135446Strhodes
561135446Strhodes	result = dst_key_fromnamedfile(keyfile,
562135446Strhodes				       DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
563135446Strhodes				       &dstkey);
564135446Strhodes	if (result != ISC_R_SUCCESS) {
565135446Strhodes		fprintf(stderr, "could not read key from %s: %s\n",
566135446Strhodes			keyfile, isc_result_totext(result));
567135446Strhodes		return;
568135446Strhodes	}
569170222Sdougb	switch (dst_key_alg(dstkey)) {
570170222Sdougb	case DST_ALG_HMACMD5:
571170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
572170222Sdougb		break;
573170222Sdougb	case DST_ALG_HMACSHA1:
574170222Sdougb		hmacname = DNS_TSIG_HMACSHA1_NAME;
575170222Sdougb		break;
576170222Sdougb	case DST_ALG_HMACSHA224:
577170222Sdougb		hmacname = DNS_TSIG_HMACSHA224_NAME;
578170222Sdougb		break;
579170222Sdougb	case DST_ALG_HMACSHA256:
580170222Sdougb		hmacname = DNS_TSIG_HMACSHA256_NAME;
581170222Sdougb		break;
582170222Sdougb	case DST_ALG_HMACSHA384:
583170222Sdougb		hmacname = DNS_TSIG_HMACSHA384_NAME;
584170222Sdougb		break;
585170222Sdougb	case DST_ALG_HMACSHA512:
586170222Sdougb		hmacname = DNS_TSIG_HMACSHA512_NAME;
587170222Sdougb		break;
588170222Sdougb	}
589170222Sdougb	if (hmacname != NULL) {
590135446Strhodes		result = dns_tsigkey_createfromkey(dst_key_name(dstkey),
591170222Sdougb						   hmacname, dstkey, ISC_FALSE,
592170222Sdougb						   NULL, 0, 0, mctx, NULL,
593170222Sdougb						   &tsigkey);
594135446Strhodes		if (result != ISC_R_SUCCESS) {
595135446Strhodes			fprintf(stderr, "could not create key from %s: %s\n",
596135446Strhodes				keyfile, isc_result_totext(result));
597135446Strhodes			dst_key_free(&dstkey);
598135446Strhodes			return;
599135446Strhodes		}
600135446Strhodes	} else
601135446Strhodes		sig0key = dstkey;
602135446Strhodes}
603135446Strhodes
604135446Strhodesstatic void
605135446Strhodesdoshutdown(void) {
606135446Strhodes	isc_task_detach(&global_task);
607135446Strhodes
608135446Strhodes	if (userserver != NULL)
609135446Strhodes		isc_mem_put(mctx, userserver, sizeof(isc_sockaddr_t));
610135446Strhodes
611135446Strhodes	if (localaddr != NULL)
612135446Strhodes		isc_mem_put(mctx, localaddr, sizeof(isc_sockaddr_t));
613135446Strhodes
614135446Strhodes	if (tsigkey != NULL) {
615135446Strhodes		ddebug("Freeing TSIG key");
616135446Strhodes		dns_tsigkey_detach(&tsigkey);
617135446Strhodes	}
618135446Strhodes
619135446Strhodes	if (sig0key != NULL) {
620135446Strhodes		ddebug("Freeing SIG(0) key");
621135446Strhodes		dst_key_free(&sig0key);
622135446Strhodes	}
623135446Strhodes
624135446Strhodes	if (updatemsg != NULL)
625135446Strhodes		dns_message_destroy(&updatemsg);
626135446Strhodes
627135446Strhodes	if (is_dst_up) {
628135446Strhodes		ddebug("Destroy DST lib");
629135446Strhodes		dst_lib_destroy();
630135446Strhodes		is_dst_up = ISC_FALSE;
631135446Strhodes	}
632135446Strhodes
633193149Sdougb	cleanup_entropy(&entropy);
634135446Strhodes
635135446Strhodes	lwres_conf_clear(lwctx);
636135446Strhodes	lwres_context_destroy(&lwctx);
637135446Strhodes
638135446Strhodes	isc_mem_put(mctx, servers, ns_total * sizeof(isc_sockaddr_t));
639135446Strhodes
640135446Strhodes	ddebug("Destroying request manager");
641135446Strhodes	dns_requestmgr_detach(&requestmgr);
642135446Strhodes
643135446Strhodes	ddebug("Freeing the dispatchers");
644135446Strhodes	if (have_ipv4)
645135446Strhodes		dns_dispatch_detach(&dispatchv4);
646135446Strhodes	if (have_ipv6)
647135446Strhodes		dns_dispatch_detach(&dispatchv6);
648135446Strhodes
649135446Strhodes	ddebug("Shutting down dispatch manager");
650135446Strhodes	dns_dispatchmgr_destroy(&dispatchmgr);
651135446Strhodes
652135446Strhodes}
653135446Strhodes
654135446Strhodesstatic void
655135446Strhodesmaybeshutdown(void) {
656135446Strhodes	ddebug("Shutting down request manager");
657135446Strhodes	dns_requestmgr_shutdown(requestmgr);
658135446Strhodes
659135446Strhodes	if (requests != 0)
660135446Strhodes		return;
661135446Strhodes
662135446Strhodes	doshutdown();
663135446Strhodes}
664135446Strhodes
665135446Strhodesstatic void
666135446Strhodesshutdown_program(isc_task_t *task, isc_event_t *event) {
667135446Strhodes	REQUIRE(task == global_task);
668135446Strhodes	UNUSED(task);
669135446Strhodes
670135446Strhodes	ddebug("shutdown_program()");
671135446Strhodes	isc_event_free(&event);
672135446Strhodes
673135446Strhodes	shuttingdown = ISC_TRUE;
674135446Strhodes	maybeshutdown();
675135446Strhodes}
676135446Strhodes
677135446Strhodesstatic void
678135446Strhodessetup_system(void) {
679135446Strhodes	isc_result_t result;
680135446Strhodes	isc_sockaddr_t bind_any, bind_any6;
681135446Strhodes	lwres_result_t lwresult;
682135446Strhodes	unsigned int attrs, attrmask;
683135446Strhodes	int i;
684193149Sdougb	isc_logconfig_t *logconfig = NULL;
685135446Strhodes
686135446Strhodes	ddebug("setup_system()");
687135446Strhodes
688135446Strhodes	dns_result_register();
689135446Strhodes
690135446Strhodes	result = isc_net_probeipv4();
691135446Strhodes	if (result == ISC_R_SUCCESS)
692135446Strhodes		have_ipv4 = ISC_TRUE;
693135446Strhodes
694135446Strhodes	result = isc_net_probeipv6();
695135446Strhodes	if (result == ISC_R_SUCCESS)
696135446Strhodes		have_ipv6 = ISC_TRUE;
697135446Strhodes
698135446Strhodes	if (!have_ipv4 && !have_ipv6)
699135446Strhodes		fatal("could not find either IPv4 or IPv6");
700135446Strhodes
701193149Sdougb	result = isc_log_create(mctx, &lctx, &logconfig);
702193149Sdougb	check_result(result, "isc_log_create");
703135446Strhodes
704193149Sdougb	isc_log_setcontext(lctx);
705193149Sdougb	dns_log_init(lctx);
706193149Sdougb	dns_log_setcontext(lctx);
707193149Sdougb
708193149Sdougb	result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
709193149Sdougb	check_result(result, "isc_log_usechannel");
710193149Sdougb
711193149Sdougb	isc_log_setdebuglevel(lctx, logdebuglevel);
712193149Sdougb
713135446Strhodes	lwresult = lwres_context_create(&lwctx, mctx, mem_alloc, mem_free, 1);
714135446Strhodes	if (lwresult != LWRES_R_SUCCESS)
715135446Strhodes		fatal("lwres_context_create failed");
716135446Strhodes
717135446Strhodes	(void)lwres_conf_parse(lwctx, RESOLV_CONF);
718135446Strhodes	lwconf = lwres_conf_get(lwctx);
719135446Strhodes
720135446Strhodes	ns_total = lwconf->nsnext;
721135446Strhodes	if (ns_total <= 0) {
722135446Strhodes		/* No name servers in resolv.conf; default to loopback. */
723135446Strhodes		struct in_addr localhost;
724135446Strhodes		ns_total = 1;
725135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
726135446Strhodes		if (servers == NULL)
727135446Strhodes			fatal("out of memory");
728135446Strhodes		localhost.s_addr = htonl(INADDR_LOOPBACK);
729135446Strhodes		isc_sockaddr_fromin(&servers[0], &localhost, DNSDEFAULTPORT);
730135446Strhodes	} else {
731135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
732135446Strhodes		if (servers == NULL)
733135446Strhodes			fatal("out of memory");
734135446Strhodes		for (i = 0; i < ns_total; i++) {
735135446Strhodes			if (lwconf->nameservers[i].family == LWRES_ADDRTYPE_V4) {
736135446Strhodes				struct in_addr in4;
737135446Strhodes				memcpy(&in4, lwconf->nameservers[i].address, 4);
738135446Strhodes				isc_sockaddr_fromin(&servers[i], &in4, DNSDEFAULTPORT);
739135446Strhodes			} else {
740135446Strhodes				struct in6_addr in6;
741135446Strhodes				memcpy(&in6, lwconf->nameservers[i].address, 16);
742135446Strhodes				isc_sockaddr_fromin6(&servers[i], &in6,
743135446Strhodes						     DNSDEFAULTPORT);
744135446Strhodes			}
745135446Strhodes		}
746135446Strhodes	}
747135446Strhodes
748193149Sdougb	setup_entropy(mctx, NULL, &entropy);
749135446Strhodes
750193149Sdougb	result = isc_hash_create(mctx, entropy, DNS_NAME_MAXWIRE);
751135446Strhodes	check_result(result, "isc_hash_create");
752135446Strhodes	isc_hash_init();
753135446Strhodes
754193149Sdougb	result = dns_dispatchmgr_create(mctx, entropy, &dispatchmgr);
755135446Strhodes	check_result(result, "dns_dispatchmgr_create");
756135446Strhodes
757135446Strhodes	result = isc_socketmgr_create(mctx, &socketmgr);
758135446Strhodes	check_result(result, "dns_socketmgr_create");
759135446Strhodes
760135446Strhodes	result = isc_timermgr_create(mctx, &timermgr);
761135446Strhodes	check_result(result, "dns_timermgr_create");
762135446Strhodes
763135446Strhodes	result = isc_taskmgr_create(mctx, 1, 0, &taskmgr);
764135446Strhodes	check_result(result, "isc_taskmgr_create");
765135446Strhodes
766135446Strhodes	result = isc_task_create(taskmgr, 0, &global_task);
767135446Strhodes	check_result(result, "isc_task_create");
768135446Strhodes
769135446Strhodes	result = isc_task_onshutdown(global_task, shutdown_program, NULL);
770135446Strhodes	check_result(result, "isc_task_onshutdown");
771135446Strhodes
772193149Sdougb	result = dst_lib_init(mctx, entropy, 0);
773135446Strhodes	check_result(result, "dst_lib_init");
774135446Strhodes	is_dst_up = ISC_TRUE;
775135446Strhodes
776135446Strhodes	attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
777135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
778135446Strhodes
779135446Strhodes	if (have_ipv6) {
780135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
781135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
782135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
783135446Strhodes		isc_sockaddr_any6(&bind_any6);
784135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
785135446Strhodes					     &bind_any6, PACKETSIZE,
786135446Strhodes					     4, 2, 3, 5,
787135446Strhodes					     attrs, attrmask, &dispatchv6);
788135446Strhodes		check_result(result, "dns_dispatch_getudp (v6)");
789135446Strhodes	}
790135446Strhodes
791135446Strhodes	if (have_ipv4) {
792135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
793135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
794135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
795135446Strhodes		isc_sockaddr_any(&bind_any);
796135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
797135446Strhodes					     &bind_any, PACKETSIZE,
798135446Strhodes					     4, 2, 3, 5,
799135446Strhodes					     attrs, attrmask, &dispatchv4);
800135446Strhodes		check_result(result, "dns_dispatch_getudp (v4)");
801135446Strhodes	}
802135446Strhodes
803135446Strhodes	result = dns_requestmgr_create(mctx, timermgr,
804135446Strhodes				       socketmgr, taskmgr, dispatchmgr,
805135446Strhodes				       dispatchv4, dispatchv6, &requestmgr);
806135446Strhodes	check_result(result, "dns_requestmgr_create");
807135446Strhodes
808135446Strhodes	if (keystr != NULL)
809135446Strhodes		setup_keystr();
810135446Strhodes	else if (keyfile != NULL)
811135446Strhodes		setup_keyfile();
812135446Strhodes}
813135446Strhodes
814135446Strhodesstatic void
815135446Strhodesget_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
816135446Strhodes	int count;
817135446Strhodes	isc_result_t result;
818135446Strhodes
819135446Strhodes	isc_app_block();
820135446Strhodes	result = bind9_getaddresses(host, port, sockaddr, 1, &count);
821135446Strhodes	isc_app_unblock();
822135446Strhodes	if (result != ISC_R_SUCCESS)
823135446Strhodes		fatal("couldn't get address for '%s': %s",
824135446Strhodes		      host, isc_result_totext(result));
825135446Strhodes	INSIST(count == 1);
826135446Strhodes}
827135446Strhodes
828193149Sdougb#define PARSE_ARGS_FMT "dDMl:y:govk:rR::t:u:"
829193149Sdougb
830135446Strhodesstatic void
831193149Sdougbpre_parse_args(int argc, char **argv) {
832135446Strhodes	int ch;
833193149Sdougb
834193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
835193149Sdougb		switch (ch) {
836193149Sdougb		case 'M': /* was -dm */
837193149Sdougb			debugging = ISC_TRUE;
838193149Sdougb			ddebugging = ISC_TRUE;
839193149Sdougb			memdebugging = ISC_TRUE;
840193149Sdougb			isc_mem_debugging = ISC_MEM_DEBUGTRACE |
841193149Sdougb					    ISC_MEM_DEBUGRECORD;
842193149Sdougb			break;
843193149Sdougb
844193149Sdougb		case '?':
845193149Sdougb			if (isc_commandline_option != '?')
846193149Sdougb				fprintf(stderr, "%s: invalid argument -%c\n",
847193149Sdougb					argv[0], isc_commandline_option);
848193149Sdougb			fprintf(stderr, "usage: nsupdate [-d] "
849193149Sdougb				"[-g | -o | -y keyname:secret | -k keyfile] "
850193149Sdougb				"[-v] [filename]\n");
851193149Sdougb			exit(1);
852193149Sdougb
853193149Sdougb		default:
854193149Sdougb			break;
855193149Sdougb		}
856193149Sdougb	}
857193149Sdougb	isc_commandline_reset = ISC_TRUE;
858193149Sdougb	isc_commandline_index = 1;
859193149Sdougb}
860193149Sdougb
861193149Sdougbstatic void
862193149Sdougbparse_args(int argc, char **argv, isc_mem_t *mctx, isc_entropy_t **ectx) {
863193149Sdougb	int ch;
864193149Sdougb	isc_uint32_t i;
865135446Strhodes	isc_result_t result;
866135446Strhodes
867135446Strhodes	debug("parse_args");
868193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
869135446Strhodes		switch (ch) {
870135446Strhodes		case 'd':
871135446Strhodes			debugging = ISC_TRUE;
872135446Strhodes			break;
873135446Strhodes		case 'D': /* was -dd */
874135446Strhodes			debugging = ISC_TRUE;
875135446Strhodes			ddebugging = ISC_TRUE;
876135446Strhodes			break;
877193149Sdougb		case 'M':
878135446Strhodes			break;
879193149Sdougb		case 'l':
880193149Sdougb			result = isc_parse_uint32(&i, isc_commandline_argument,
881193149Sdougb						  10);
882193149Sdougb			if (result != ISC_R_SUCCESS) {
883193149Sdougb				fprintf(stderr, "bad library debug value "
884193149Sdougb					"'%s'\n", isc_commandline_argument);
885193149Sdougb				exit(1);
886193149Sdougb			}
887193149Sdougb			logdebuglevel = i;
888193149Sdougb			break;
889135446Strhodes		case 'y':
890135446Strhodes			keystr = isc_commandline_argument;
891135446Strhodes			break;
892135446Strhodes		case 'v':
893135446Strhodes			usevc = ISC_TRUE;
894135446Strhodes			break;
895135446Strhodes		case 'k':
896135446Strhodes			keyfile = isc_commandline_argument;
897135446Strhodes			break;
898193149Sdougb		case 'g':
899193149Sdougb			usegsstsig = ISC_TRUE;
900193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
901193149Sdougb			break;
902193149Sdougb		case 'o':
903193149Sdougb			usegsstsig = ISC_TRUE;
904193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
905193149Sdougb			break;
906135446Strhodes		case 't':
907135446Strhodes			result = isc_parse_uint32(&timeout,
908135446Strhodes						  isc_commandline_argument, 10);
909135446Strhodes			if (result != ISC_R_SUCCESS) {
910135446Strhodes				fprintf(stderr, "bad timeout '%s'\n",						isc_commandline_argument);
911135446Strhodes				exit(1);
912135446Strhodes			}
913135446Strhodes			if (timeout == 0)
914143731Sdougb				timeout = UINT_MAX;
915135446Strhodes			break;
916135446Strhodes		case 'u':
917135446Strhodes			result = isc_parse_uint32(&udp_timeout,
918135446Strhodes						  isc_commandline_argument, 10);
919135446Strhodes			if (result != ISC_R_SUCCESS) {
920135446Strhodes				fprintf(stderr, "bad udp timeout '%s'\n",						isc_commandline_argument);
921135446Strhodes				exit(1);
922135446Strhodes			}
923135446Strhodes			if (udp_timeout == 0)
924143731Sdougb				udp_timeout = UINT_MAX;
925135446Strhodes			break;
926135446Strhodes		case 'r':
927135446Strhodes			result = isc_parse_uint32(&udp_retries,
928135446Strhodes						  isc_commandline_argument, 10);
929135446Strhodes			if (result != ISC_R_SUCCESS) {
930135446Strhodes				fprintf(stderr, "bad udp retries '%s'\n",						isc_commandline_argument);
931135446Strhodes				exit(1);
932135446Strhodes			}
933135446Strhodes			break;
934193149Sdougb
935193149Sdougb		case 'R':
936193149Sdougb			setup_entropy(mctx, isc_commandline_argument, ectx);
937193149Sdougb			break;
938193149Sdougb
939135446Strhodes		default:
940193149Sdougb			fprintf(stderr, "%s: unhandled option: %c\n",
941193149Sdougb				argv[0], isc_commandline_option);
942135446Strhodes			exit(1);
943135446Strhodes		}
944135446Strhodes	}
945135446Strhodes	if (keyfile != NULL && keystr != NULL) {
946135446Strhodes		fprintf(stderr, "%s: cannot specify both -k and -y\n",
947135446Strhodes			argv[0]);
948135446Strhodes		exit(1);
949135446Strhodes	}
950135446Strhodes
951193149Sdougb#ifdef GSSAPI
952193149Sdougb	if (usegsstsig && (keyfile != NULL || keystr != NULL)) {
953193149Sdougb		fprintf(stderr, "%s: cannot specify -g with -k or -y\n",
954193149Sdougb			argv[0]);
955193149Sdougb		exit(1);
956193149Sdougb	}
957193149Sdougb#else
958193149Sdougb	if (usegsstsig) {
959193149Sdougb		fprintf(stderr, "%s: cannot specify -g  or -o, " \
960193149Sdougb			"program not linked with GSS API Library\n",
961193149Sdougb			argv[0]);
962193149Sdougb		exit(1);
963193149Sdougb	}
964193149Sdougb#endif
965193149Sdougb
966135446Strhodes	if (argv[isc_commandline_index] != NULL) {
967135446Strhodes		if (strcmp(argv[isc_commandline_index], "-") == 0) {
968135446Strhodes			input = stdin;
969135446Strhodes		} else {
970135446Strhodes			result = isc_stdio_open(argv[isc_commandline_index],
971135446Strhodes						"r", &input);
972135446Strhodes			if (result != ISC_R_SUCCESS) {
973135446Strhodes				fprintf(stderr, "could not open '%s': %s\n",
974135446Strhodes					argv[isc_commandline_index],
975135446Strhodes					isc_result_totext(result));
976135446Strhodes				exit(1);
977135446Strhodes			}
978135446Strhodes		}
979135446Strhodes		interactive = ISC_FALSE;
980135446Strhodes	}
981135446Strhodes}
982135446Strhodes
983135446Strhodesstatic isc_uint16_t
984135446Strhodesparse_name(char **cmdlinep, dns_message_t *msg, dns_name_t **namep) {
985135446Strhodes	isc_result_t result;
986135446Strhodes	char *word;
987135446Strhodes	isc_buffer_t *namebuf = NULL;
988135446Strhodes	isc_buffer_t source;
989135446Strhodes
990135446Strhodes	word = nsu_strsep(cmdlinep, " \t\r\n");
991135446Strhodes	if (*word == 0) {
992135446Strhodes		fprintf(stderr, "could not read owner name\n");
993135446Strhodes		return (STATUS_SYNTAX);
994135446Strhodes	}
995135446Strhodes
996135446Strhodes	result = dns_message_gettempname(msg, namep);
997135446Strhodes	check_result(result, "dns_message_gettempname");
998135446Strhodes	result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
999135446Strhodes	check_result(result, "isc_buffer_allocate");
1000135446Strhodes	dns_name_init(*namep, NULL);
1001135446Strhodes	dns_name_setbuffer(*namep, namebuf);
1002135446Strhodes	dns_message_takebuffer(msg, &namebuf);
1003135446Strhodes	isc_buffer_init(&source, word, strlen(word));
1004135446Strhodes	isc_buffer_add(&source, strlen(word));
1005135446Strhodes	result = dns_name_fromtext(*namep, &source, dns_rootname,
1006135446Strhodes				   ISC_FALSE, NULL);
1007135446Strhodes	check_result(result, "dns_name_fromtext");
1008135446Strhodes	isc_buffer_invalidate(&source);
1009135446Strhodes	return (STATUS_MORE);
1010135446Strhodes}
1011135446Strhodes
1012135446Strhodesstatic isc_uint16_t
1013135446Strhodesparse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass,
1014135446Strhodes	    dns_rdatatype_t rdatatype, dns_message_t *msg,
1015135446Strhodes	    dns_rdata_t *rdata)
1016135446Strhodes{
1017135446Strhodes	char *cmdline = *cmdlinep;
1018135446Strhodes	isc_buffer_t source, *buf = NULL, *newbuf = NULL;
1019135446Strhodes	isc_region_t r;
1020135446Strhodes	isc_lex_t *lex = NULL;
1021135446Strhodes	dns_rdatacallbacks_t callbacks;
1022135446Strhodes	isc_result_t result;
1023135446Strhodes
1024135446Strhodes	while (*cmdline != 0 && isspace((unsigned char)*cmdline))
1025135446Strhodes		cmdline++;
1026135446Strhodes
1027135446Strhodes	if (*cmdline != 0) {
1028135446Strhodes		dns_rdatacallbacks_init(&callbacks);
1029135446Strhodes		result = isc_lex_create(mctx, strlen(cmdline), &lex);
1030135446Strhodes		check_result(result, "isc_lex_create");
1031135446Strhodes		isc_buffer_init(&source, cmdline, strlen(cmdline));
1032135446Strhodes		isc_buffer_add(&source, strlen(cmdline));
1033135446Strhodes		result = isc_lex_openbuffer(lex, &source);
1034135446Strhodes		check_result(result, "isc_lex_openbuffer");
1035135446Strhodes		result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
1036135446Strhodes		check_result(result, "isc_buffer_allocate");
1037193149Sdougb		result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex,
1038135446Strhodes					    dns_rootname, 0, mctx, buf,
1039135446Strhodes					    &callbacks);
1040135446Strhodes		isc_lex_destroy(&lex);
1041135446Strhodes		if (result == ISC_R_SUCCESS) {
1042135446Strhodes			isc_buffer_usedregion(buf, &r);
1043135446Strhodes			result = isc_buffer_allocate(mctx, &newbuf, r.length);
1044135446Strhodes			check_result(result, "isc_buffer_allocate");
1045135446Strhodes			isc_buffer_putmem(newbuf, r.base, r.length);
1046135446Strhodes			isc_buffer_usedregion(newbuf, &r);
1047135446Strhodes			dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
1048135446Strhodes			isc_buffer_free(&buf);
1049135446Strhodes			dns_message_takebuffer(msg, &newbuf);
1050135446Strhodes		} else {
1051135446Strhodes			fprintf(stderr, "invalid rdata format: %s\n",
1052135446Strhodes				isc_result_totext(result));
1053135446Strhodes			isc_buffer_free(&buf);
1054135446Strhodes			return (STATUS_SYNTAX);
1055135446Strhodes		}
1056135446Strhodes	} else {
1057135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1058135446Strhodes	}
1059135446Strhodes	*cmdlinep = cmdline;
1060135446Strhodes	return (STATUS_MORE);
1061135446Strhodes}
1062135446Strhodes
1063135446Strhodesstatic isc_uint16_t
1064135446Strhodesmake_prereq(char *cmdline, isc_boolean_t ispositive, isc_boolean_t isrrset) {
1065135446Strhodes	isc_result_t result;
1066135446Strhodes	char *word;
1067135446Strhodes	dns_name_t *name = NULL;
1068135446Strhodes	isc_textregion_t region;
1069135446Strhodes	dns_rdataset_t *rdataset = NULL;
1070135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1071135446Strhodes	dns_rdataclass_t rdataclass;
1072135446Strhodes	dns_rdatatype_t rdatatype;
1073135446Strhodes	dns_rdata_t *rdata = NULL;
1074135446Strhodes	isc_uint16_t retval;
1075135446Strhodes
1076135446Strhodes	ddebug("make_prereq()");
1077135446Strhodes
1078135446Strhodes	/*
1079135446Strhodes	 * Read the owner name
1080135446Strhodes	 */
1081135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1082135446Strhodes	if (retval != STATUS_MORE)
1083135446Strhodes		return (retval);
1084135446Strhodes
1085135446Strhodes	/*
1086135446Strhodes	 * If this is an rrset prereq, read the class or type.
1087135446Strhodes	 */
1088135446Strhodes	if (isrrset) {
1089135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1090135446Strhodes		if (*word == 0) {
1091135446Strhodes			fprintf(stderr, "could not read class or type\n");
1092135446Strhodes			goto failure;
1093135446Strhodes		}
1094135446Strhodes		region.base = word;
1095135446Strhodes		region.length = strlen(word);
1096135446Strhodes		result = dns_rdataclass_fromtext(&rdataclass, &region);
1097135446Strhodes		if (result == ISC_R_SUCCESS) {
1098135446Strhodes			if (!setzoneclass(rdataclass)) {
1099135446Strhodes				fprintf(stderr, "class mismatch: %s\n", word);
1100135446Strhodes				goto failure;
1101135446Strhodes			}
1102135446Strhodes			/*
1103135446Strhodes			 * Now read the type.
1104135446Strhodes			 */
1105135446Strhodes			word = nsu_strsep(&cmdline, " \t\r\n");
1106135446Strhodes			if (*word == 0) {
1107135446Strhodes				fprintf(stderr, "could not read type\n");
1108135446Strhodes				goto failure;
1109135446Strhodes			}
1110135446Strhodes			region.base = word;
1111135446Strhodes			region.length = strlen(word);
1112135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1113135446Strhodes			if (result != ISC_R_SUCCESS) {
1114135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1115135446Strhodes				goto failure;
1116135446Strhodes			}
1117135446Strhodes		} else {
1118135446Strhodes			rdataclass = getzoneclass();
1119135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1120135446Strhodes			if (result != ISC_R_SUCCESS) {
1121135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1122135446Strhodes				goto failure;
1123135446Strhodes			}
1124135446Strhodes		}
1125135446Strhodes	} else
1126135446Strhodes		rdatatype = dns_rdatatype_any;
1127135446Strhodes
1128135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1129135446Strhodes	check_result(result, "dns_message_gettemprdata");
1130135446Strhodes
1131193149Sdougb	dns_rdata_init(rdata);
1132135446Strhodes
1133135446Strhodes	if (isrrset && ispositive) {
1134135446Strhodes		retval = parse_rdata(&cmdline, rdataclass, rdatatype,
1135135446Strhodes				     updatemsg, rdata);
1136135446Strhodes		if (retval != STATUS_MORE)
1137135446Strhodes			goto failure;
1138135446Strhodes	} else
1139135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1140135446Strhodes
1141135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1142135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1143135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1144135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1145135446Strhodes	dns_rdatalist_init(rdatalist);
1146135446Strhodes	rdatalist->type = rdatatype;
1147135446Strhodes	if (ispositive) {
1148135446Strhodes		if (isrrset && rdata->data != NULL)
1149135446Strhodes			rdatalist->rdclass = rdataclass;
1150135446Strhodes		else
1151135446Strhodes			rdatalist->rdclass = dns_rdataclass_any;
1152135446Strhodes	} else
1153135446Strhodes		rdatalist->rdclass = dns_rdataclass_none;
1154135446Strhodes	rdatalist->covers = 0;
1155135446Strhodes	rdatalist->ttl = 0;
1156135446Strhodes	rdata->rdclass = rdatalist->rdclass;
1157135446Strhodes	rdata->type = rdatatype;
1158135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1159135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1160135446Strhodes	dns_rdataset_init(rdataset);
1161135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1162135446Strhodes	ISC_LIST_INIT(name->list);
1163135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1164135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_PREREQUISITE);
1165135446Strhodes	return (STATUS_MORE);
1166135446Strhodes
1167135446Strhodes failure:
1168135446Strhodes	if (name != NULL)
1169135446Strhodes		dns_message_puttempname(updatemsg, &name);
1170135446Strhodes	return (STATUS_SYNTAX);
1171135446Strhodes}
1172135446Strhodes
1173135446Strhodesstatic isc_uint16_t
1174135446Strhodesevaluate_prereq(char *cmdline) {
1175135446Strhodes	char *word;
1176135446Strhodes	isc_boolean_t ispositive, isrrset;
1177135446Strhodes
1178135446Strhodes	ddebug("evaluate_prereq()");
1179135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1180135446Strhodes	if (*word == 0) {
1181135446Strhodes		fprintf(stderr, "could not read operation code\n");
1182135446Strhodes		return (STATUS_SYNTAX);
1183135446Strhodes	}
1184135446Strhodes	if (strcasecmp(word, "nxdomain") == 0) {
1185135446Strhodes		ispositive = ISC_FALSE;
1186135446Strhodes		isrrset = ISC_FALSE;
1187135446Strhodes	} else if (strcasecmp(word, "yxdomain") == 0) {
1188135446Strhodes		ispositive = ISC_TRUE;
1189135446Strhodes		isrrset = ISC_FALSE;
1190135446Strhodes	} else if (strcasecmp(word, "nxrrset") == 0) {
1191135446Strhodes		ispositive = ISC_FALSE;
1192135446Strhodes		isrrset = ISC_TRUE;
1193135446Strhodes	} else if (strcasecmp(word, "yxrrset") == 0) {
1194135446Strhodes		ispositive = ISC_TRUE;
1195135446Strhodes		isrrset = ISC_TRUE;
1196135446Strhodes	} else {
1197135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1198135446Strhodes		return (STATUS_SYNTAX);
1199135446Strhodes	}
1200135446Strhodes	return (make_prereq(cmdline, ispositive, isrrset));
1201135446Strhodes}
1202135446Strhodes
1203135446Strhodesstatic isc_uint16_t
1204135446Strhodesevaluate_server(char *cmdline) {
1205135446Strhodes	char *word, *server;
1206135446Strhodes	long port;
1207135446Strhodes
1208135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1209135446Strhodes	if (*word == 0) {
1210135446Strhodes		fprintf(stderr, "could not read server name\n");
1211135446Strhodes		return (STATUS_SYNTAX);
1212135446Strhodes	}
1213135446Strhodes	server = word;
1214135446Strhodes
1215135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1216135446Strhodes	if (*word == 0)
1217135446Strhodes		port = DNSDEFAULTPORT;
1218135446Strhodes	else {
1219135446Strhodes		char *endp;
1220135446Strhodes		port = strtol(word, &endp, 10);
1221135446Strhodes		if (*endp != 0) {
1222135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1223135446Strhodes			return (STATUS_SYNTAX);
1224135446Strhodes		} else if (port < 1 || port > 65535) {
1225135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1226135446Strhodes				"(1 to 65535)\n", word);
1227135446Strhodes			return (STATUS_SYNTAX);
1228135446Strhodes		}
1229135446Strhodes	}
1230135446Strhodes
1231135446Strhodes	if (userserver == NULL) {
1232135446Strhodes		userserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1233135446Strhodes		if (userserver == NULL)
1234135446Strhodes			fatal("out of memory");
1235135446Strhodes	}
1236135446Strhodes
1237135446Strhodes	get_address(server, (in_port_t)port, userserver);
1238135446Strhodes
1239135446Strhodes	return (STATUS_MORE);
1240135446Strhodes}
1241135446Strhodes
1242135446Strhodesstatic isc_uint16_t
1243135446Strhodesevaluate_local(char *cmdline) {
1244135446Strhodes	char *word, *local;
1245135446Strhodes	long port;
1246135446Strhodes	struct in_addr in4;
1247135446Strhodes	struct in6_addr in6;
1248135446Strhodes
1249135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1250135446Strhodes	if (*word == 0) {
1251135446Strhodes		fprintf(stderr, "could not read server name\n");
1252135446Strhodes		return (STATUS_SYNTAX);
1253135446Strhodes	}
1254135446Strhodes	local = word;
1255135446Strhodes
1256135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1257135446Strhodes	if (*word == 0)
1258135446Strhodes		port = 0;
1259135446Strhodes	else {
1260135446Strhodes		char *endp;
1261135446Strhodes		port = strtol(word, &endp, 10);
1262135446Strhodes		if (*endp != 0) {
1263135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1264135446Strhodes			return (STATUS_SYNTAX);
1265135446Strhodes		} else if (port < 1 || port > 65535) {
1266135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1267135446Strhodes				"(1 to 65535)\n", word);
1268135446Strhodes			return (STATUS_SYNTAX);
1269135446Strhodes		}
1270135446Strhodes	}
1271135446Strhodes
1272135446Strhodes	if (localaddr == NULL) {
1273135446Strhodes		localaddr = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1274135446Strhodes		if (localaddr == NULL)
1275135446Strhodes			fatal("out of memory");
1276135446Strhodes	}
1277135446Strhodes
1278135446Strhodes	if (have_ipv6 && inet_pton(AF_INET6, local, &in6) == 1)
1279135446Strhodes		isc_sockaddr_fromin6(localaddr, &in6, (in_port_t)port);
1280135446Strhodes	else if (have_ipv4 && inet_pton(AF_INET, local, &in4) == 1)
1281135446Strhodes		isc_sockaddr_fromin(localaddr, &in4, (in_port_t)port);
1282135446Strhodes	else {
1283135446Strhodes		fprintf(stderr, "invalid address %s", local);
1284135446Strhodes		return (STATUS_SYNTAX);
1285135446Strhodes	}
1286135446Strhodes
1287135446Strhodes	return (STATUS_MORE);
1288135446Strhodes}
1289135446Strhodes
1290135446Strhodesstatic isc_uint16_t
1291135446Strhodesevaluate_key(char *cmdline) {
1292135446Strhodes	char *namestr;
1293135446Strhodes	char *secretstr;
1294135446Strhodes	isc_buffer_t b;
1295135446Strhodes	isc_result_t result;
1296135446Strhodes	dns_fixedname_t fkeyname;
1297135446Strhodes	dns_name_t *keyname;
1298135446Strhodes	int secretlen;
1299135446Strhodes	unsigned char *secret = NULL;
1300135446Strhodes	isc_buffer_t secretbuf;
1301170222Sdougb	dns_name_t *hmacname = NULL;
1302170222Sdougb	isc_uint16_t digestbits = 0;
1303170222Sdougb	char *n;
1304135446Strhodes
1305135446Strhodes	namestr = nsu_strsep(&cmdline, " \t\r\n");
1306135446Strhodes	if (*namestr == 0) {
1307135446Strhodes		fprintf(stderr, "could not read key name\n");
1308135446Strhodes		return (STATUS_SYNTAX);
1309135446Strhodes	}
1310135446Strhodes
1311135446Strhodes	dns_fixedname_init(&fkeyname);
1312135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
1313135446Strhodes
1314170222Sdougb	n = strchr(namestr, ':');
1315170222Sdougb	if (n != NULL) {
1316170222Sdougb		digestbits = parse_hmac(&hmacname, namestr, n - namestr);
1317170222Sdougb		namestr = n + 1;
1318170222Sdougb	} else
1319170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
1320170222Sdougb
1321135446Strhodes	isc_buffer_init(&b, namestr, strlen(namestr));
1322135446Strhodes	isc_buffer_add(&b, strlen(namestr));
1323135446Strhodes	result = dns_name_fromtext(keyname, &b, dns_rootname, ISC_FALSE, NULL);
1324135446Strhodes	if (result != ISC_R_SUCCESS) {
1325135446Strhodes		fprintf(stderr, "could not parse key name\n");
1326135446Strhodes		return (STATUS_SYNTAX);
1327135446Strhodes	}
1328135446Strhodes
1329135446Strhodes	secretstr = nsu_strsep(&cmdline, "\r\n");
1330135446Strhodes	if (*secretstr == 0) {
1331135446Strhodes		fprintf(stderr, "could not read key secret\n");
1332135446Strhodes		return (STATUS_SYNTAX);
1333135446Strhodes	}
1334135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
1335135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
1336135446Strhodes	if (secret == NULL)
1337135446Strhodes		fatal("out of memory");
1338186462Sdougb
1339135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
1340135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
1341135446Strhodes	if (result != ISC_R_SUCCESS) {
1342135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
1343135446Strhodes			secretstr, isc_result_totext(result));
1344135446Strhodes		isc_mem_free(mctx, secret);
1345135446Strhodes		return (STATUS_SYNTAX);
1346135446Strhodes	}
1347135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
1348135446Strhodes
1349135446Strhodes	if (tsigkey != NULL)
1350135446Strhodes		dns_tsigkey_detach(&tsigkey);
1351170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
1352170222Sdougb				    ISC_TRUE, NULL, 0, 0, mctx, NULL,
1353170222Sdougb				    &tsigkey);
1354135446Strhodes	isc_mem_free(mctx, secret);
1355135446Strhodes	if (result != ISC_R_SUCCESS) {
1356135446Strhodes		fprintf(stderr, "could not create key from %s %s: %s\n",
1357135446Strhodes			namestr, secretstr, dns_result_totext(result));
1358135446Strhodes		return (STATUS_SYNTAX);
1359135446Strhodes	}
1360170222Sdougb	dst_key_setbits(tsigkey->key, digestbits);
1361135446Strhodes	return (STATUS_MORE);
1362135446Strhodes}
1363135446Strhodes
1364135446Strhodesstatic isc_uint16_t
1365135446Strhodesevaluate_zone(char *cmdline) {
1366135446Strhodes	char *word;
1367135446Strhodes	isc_buffer_t b;
1368135446Strhodes	isc_result_t result;
1369135446Strhodes
1370135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1371135446Strhodes	if (*word == 0) {
1372135446Strhodes		fprintf(stderr, "could not read zone name\n");
1373135446Strhodes		return (STATUS_SYNTAX);
1374135446Strhodes	}
1375135446Strhodes
1376135446Strhodes	dns_fixedname_init(&fuserzone);
1377135446Strhodes	userzone = dns_fixedname_name(&fuserzone);
1378135446Strhodes	isc_buffer_init(&b, word, strlen(word));
1379135446Strhodes	isc_buffer_add(&b, strlen(word));
1380135446Strhodes	result = dns_name_fromtext(userzone, &b, dns_rootname, ISC_FALSE,
1381135446Strhodes				   NULL);
1382135446Strhodes	if (result != ISC_R_SUCCESS) {
1383135446Strhodes		userzone = NULL; /* Lest it point to an invalid name */
1384135446Strhodes		fprintf(stderr, "could not parse zone name\n");
1385135446Strhodes		return (STATUS_SYNTAX);
1386135446Strhodes	}
1387135446Strhodes
1388135446Strhodes	return (STATUS_MORE);
1389135446Strhodes}
1390135446Strhodes
1391135446Strhodesstatic isc_uint16_t
1392193149Sdougbevaluate_ttl(char *cmdline) {
1393193149Sdougb	char *word;
1394193149Sdougb	isc_result_t result;
1395193149Sdougb	isc_uint32_t ttl;
1396193149Sdougb
1397193149Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
1398193149Sdougb	if (*word == 0) {
1399193149Sdougb		fprintf(stderr, "could not ttl\n");
1400193149Sdougb		return (STATUS_SYNTAX);
1401193149Sdougb	}
1402193149Sdougb
1403193149Sdougb	if (!strcasecmp(word, "none")) {
1404193149Sdougb		default_ttl = 0;
1405193149Sdougb		default_ttl_set = ISC_FALSE;
1406193149Sdougb		return (STATUS_MORE);
1407193149Sdougb	}
1408193149Sdougb
1409193149Sdougb	result = isc_parse_uint32(&ttl, word, 10);
1410193149Sdougb	if (result != ISC_R_SUCCESS)
1411193149Sdougb		return (STATUS_SYNTAX);
1412193149Sdougb
1413193149Sdougb	if (ttl > TTL_MAX) {
1414193149Sdougb		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1415193149Sdougb			word, TTL_MAX);
1416193149Sdougb		return (STATUS_SYNTAX);
1417193149Sdougb	}
1418193149Sdougb	default_ttl = ttl;
1419193149Sdougb	default_ttl_set = ISC_TRUE;
1420193149Sdougb
1421193149Sdougb	return (STATUS_MORE);
1422193149Sdougb}
1423193149Sdougb
1424193149Sdougbstatic isc_uint16_t
1425135446Strhodesevaluate_class(char *cmdline) {
1426135446Strhodes	char *word;
1427135446Strhodes	isc_textregion_t r;
1428135446Strhodes	isc_result_t result;
1429135446Strhodes	dns_rdataclass_t rdclass;
1430135446Strhodes
1431135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1432135446Strhodes	if (*word == 0) {
1433135446Strhodes		fprintf(stderr, "could not read class name\n");
1434135446Strhodes		return (STATUS_SYNTAX);
1435135446Strhodes	}
1436135446Strhodes
1437135446Strhodes	r.base = word;
1438186462Sdougb	r.length = strlen(word);
1439186462Sdougb	result = dns_rdataclass_fromtext(&rdclass, &r);
1440135446Strhodes	if (result != ISC_R_SUCCESS) {
1441135446Strhodes		fprintf(stderr, "could not parse class name: %s\n", word);
1442135446Strhodes		return (STATUS_SYNTAX);
1443135446Strhodes	}
1444135446Strhodes	switch (rdclass) {
1445135446Strhodes	case dns_rdataclass_none:
1446135446Strhodes	case dns_rdataclass_any:
1447135446Strhodes	case dns_rdataclass_reserved0:
1448135446Strhodes		fprintf(stderr, "bad default class: %s\n", word);
1449135446Strhodes		return (STATUS_SYNTAX);
1450135446Strhodes	default:
1451135446Strhodes		defaultclass = rdclass;
1452135446Strhodes	}
1453135446Strhodes
1454135446Strhodes	return (STATUS_MORE);
1455135446Strhodes}
1456135446Strhodes
1457135446Strhodesstatic isc_uint16_t
1458135446Strhodesupdate_addordelete(char *cmdline, isc_boolean_t isdelete) {
1459135446Strhodes	isc_result_t result;
1460135446Strhodes	dns_name_t *name = NULL;
1461135446Strhodes	isc_uint32_t ttl;
1462135446Strhodes	char *word;
1463135446Strhodes	dns_rdataclass_t rdataclass;
1464135446Strhodes	dns_rdatatype_t rdatatype;
1465135446Strhodes	dns_rdata_t *rdata = NULL;
1466135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1467135446Strhodes	dns_rdataset_t *rdataset = NULL;
1468135446Strhodes	isc_textregion_t region;
1469135446Strhodes	isc_uint16_t retval;
1470135446Strhodes
1471135446Strhodes	ddebug("update_addordelete()");
1472135446Strhodes
1473135446Strhodes	/*
1474135446Strhodes	 * Read the owner name.
1475135446Strhodes	 */
1476135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1477135446Strhodes	if (retval != STATUS_MORE)
1478135446Strhodes		return (retval);
1479135446Strhodes
1480135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1481135446Strhodes	check_result(result, "dns_message_gettemprdata");
1482135446Strhodes
1483193149Sdougb	dns_rdata_init(rdata);
1484135446Strhodes
1485135446Strhodes	/*
1486135446Strhodes	 * If this is an add, read the TTL and verify that it's in range.
1487135446Strhodes	 * If it's a delete, ignore a TTL if present (for compatibility).
1488135446Strhodes	 */
1489135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1490135446Strhodes	if (*word == 0) {
1491135446Strhodes		if (!isdelete) {
1492135446Strhodes			fprintf(stderr, "could not read owner ttl\n");
1493135446Strhodes			goto failure;
1494135446Strhodes		}
1495135446Strhodes		else {
1496135446Strhodes			ttl = 0;
1497135446Strhodes			rdataclass = dns_rdataclass_any;
1498135446Strhodes			rdatatype = dns_rdatatype_any;
1499135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1500135446Strhodes			goto doneparsing;
1501135446Strhodes		}
1502135446Strhodes	}
1503135446Strhodes	result = isc_parse_uint32(&ttl, word, 10);
1504135446Strhodes	if (result != ISC_R_SUCCESS) {
1505135446Strhodes		if (isdelete) {
1506135446Strhodes			ttl = 0;
1507135446Strhodes			goto parseclass;
1508193149Sdougb		} else if (default_ttl_set) {
1509193149Sdougb			ttl = default_ttl;
1510193149Sdougb			goto parseclass;
1511135446Strhodes		} else {
1512135446Strhodes			fprintf(stderr, "ttl '%s': %s\n", word,
1513135446Strhodes				isc_result_totext(result));
1514135446Strhodes			goto failure;
1515135446Strhodes		}
1516135446Strhodes	}
1517135446Strhodes
1518135446Strhodes	if (isdelete)
1519135446Strhodes		ttl = 0;
1520135446Strhodes	else if (ttl > TTL_MAX) {
1521135446Strhodes		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1522135446Strhodes			word, TTL_MAX);
1523135446Strhodes		goto failure;
1524135446Strhodes	}
1525135446Strhodes
1526135446Strhodes	/*
1527135446Strhodes	 * Read the class or type.
1528135446Strhodes	 */
1529135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1530135446Strhodes parseclass:
1531135446Strhodes	if (*word == 0) {
1532135446Strhodes		if (isdelete) {
1533135446Strhodes			rdataclass = dns_rdataclass_any;
1534135446Strhodes			rdatatype = dns_rdatatype_any;
1535135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1536135446Strhodes			goto doneparsing;
1537135446Strhodes		} else {
1538135446Strhodes			fprintf(stderr, "could not read class or type\n");
1539135446Strhodes			goto failure;
1540135446Strhodes		}
1541135446Strhodes	}
1542135446Strhodes	region.base = word;
1543135446Strhodes	region.length = strlen(word);
1544193149Sdougb	rdataclass = dns_rdataclass_any;
1545135446Strhodes	result = dns_rdataclass_fromtext(&rdataclass, &region);
1546193149Sdougb	if (result == ISC_R_SUCCESS && rdataclass != dns_rdataclass_any) {
1547135446Strhodes		if (!setzoneclass(rdataclass)) {
1548135446Strhodes			fprintf(stderr, "class mismatch: %s\n", word);
1549135446Strhodes			goto failure;
1550135446Strhodes		}
1551135446Strhodes		/*
1552135446Strhodes		 * Now read the type.
1553135446Strhodes		 */
1554135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1555135446Strhodes		if (*word == 0) {
1556135446Strhodes			if (isdelete) {
1557135446Strhodes				rdataclass = dns_rdataclass_any;
1558135446Strhodes				rdatatype = dns_rdatatype_any;
1559135446Strhodes				rdata->flags = DNS_RDATA_UPDATE;
1560135446Strhodes				goto doneparsing;
1561135446Strhodes			} else {
1562135446Strhodes				fprintf(stderr, "could not read type\n");
1563135446Strhodes				goto failure;
1564135446Strhodes			}
1565135446Strhodes		}
1566135446Strhodes		region.base = word;
1567135446Strhodes		region.length = strlen(word);
1568135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1569135446Strhodes		if (result != ISC_R_SUCCESS) {
1570135446Strhodes			fprintf(stderr, "'%s' is not a valid type: %s\n",
1571135446Strhodes				word, isc_result_totext(result));
1572135446Strhodes			goto failure;
1573135446Strhodes		}
1574135446Strhodes	} else {
1575135446Strhodes		rdataclass = getzoneclass();
1576135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1577135446Strhodes		if (result != ISC_R_SUCCESS) {
1578135446Strhodes			fprintf(stderr, "'%s' is not a valid class or type: "
1579135446Strhodes				"%s\n", word, isc_result_totext(result));
1580135446Strhodes			goto failure;
1581135446Strhodes		}
1582135446Strhodes	}
1583135446Strhodes
1584135446Strhodes	retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg,
1585135446Strhodes			     rdata);
1586135446Strhodes	if (retval != STATUS_MORE)
1587135446Strhodes		goto failure;
1588135446Strhodes
1589135446Strhodes	if (isdelete) {
1590135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
1591135446Strhodes			rdataclass = dns_rdataclass_any;
1592135446Strhodes		else
1593135446Strhodes			rdataclass = dns_rdataclass_none;
1594135446Strhodes	} else {
1595135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
1596135446Strhodes			fprintf(stderr, "could not read rdata\n");
1597135446Strhodes			goto failure;
1598135446Strhodes		}
1599135446Strhodes	}
1600135446Strhodes
1601135446Strhodes doneparsing:
1602135446Strhodes
1603135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1604135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1605135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1606135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1607135446Strhodes	dns_rdatalist_init(rdatalist);
1608135446Strhodes	rdatalist->type = rdatatype;
1609135446Strhodes	rdatalist->rdclass = rdataclass;
1610135446Strhodes	rdatalist->covers = rdatatype;
1611135446Strhodes	rdatalist->ttl = (dns_ttl_t)ttl;
1612135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1613135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1614135446Strhodes	dns_rdataset_init(rdataset);
1615135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1616135446Strhodes	ISC_LIST_INIT(name->list);
1617135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1618135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_UPDATE);
1619135446Strhodes	return (STATUS_MORE);
1620135446Strhodes
1621135446Strhodes failure:
1622135446Strhodes	if (name != NULL)
1623135446Strhodes		dns_message_puttempname(updatemsg, &name);
1624186462Sdougb	dns_message_puttemprdata(updatemsg, &rdata);
1625135446Strhodes	return (STATUS_SYNTAX);
1626135446Strhodes}
1627135446Strhodes
1628135446Strhodesstatic isc_uint16_t
1629135446Strhodesevaluate_update(char *cmdline) {
1630135446Strhodes	char *word;
1631135446Strhodes	isc_boolean_t isdelete;
1632135446Strhodes
1633135446Strhodes	ddebug("evaluate_update()");
1634135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1635135446Strhodes	if (*word == 0) {
1636135446Strhodes		fprintf(stderr, "could not read operation code\n");
1637135446Strhodes		return (STATUS_SYNTAX);
1638135446Strhodes	}
1639135446Strhodes	if (strcasecmp(word, "delete") == 0)
1640135446Strhodes		isdelete = ISC_TRUE;
1641135446Strhodes	else if (strcasecmp(word, "add") == 0)
1642135446Strhodes		isdelete = ISC_FALSE;
1643135446Strhodes	else {
1644135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1645135446Strhodes		return (STATUS_SYNTAX);
1646135446Strhodes	}
1647135446Strhodes	return (update_addordelete(cmdline, isdelete));
1648135446Strhodes}
1649135446Strhodes
1650135446Strhodesstatic void
1651170222Sdougbsetzone(dns_name_t *zonename) {
1652170222Sdougb	isc_result_t result;
1653170222Sdougb	dns_name_t *name = NULL;
1654170222Sdougb	dns_rdataset_t *rdataset = NULL;
1655170222Sdougb
1656170222Sdougb	result = dns_message_firstname(updatemsg, DNS_SECTION_ZONE);
1657170222Sdougb	if (result == ISC_R_SUCCESS) {
1658170222Sdougb		dns_message_currentname(updatemsg, DNS_SECTION_ZONE, &name);
1659170222Sdougb		dns_message_removename(updatemsg, name, DNS_SECTION_ZONE);
1660170222Sdougb		for (rdataset = ISC_LIST_HEAD(name->list);
1661170222Sdougb		     rdataset != NULL;
1662170222Sdougb		     rdataset = ISC_LIST_HEAD(name->list)) {
1663170222Sdougb			ISC_LIST_UNLINK(name->list, rdataset, link);
1664170222Sdougb			dns_rdataset_disassociate(rdataset);
1665170222Sdougb			dns_message_puttemprdataset(updatemsg, &rdataset);
1666170222Sdougb		}
1667170222Sdougb		dns_message_puttempname(updatemsg, &name);
1668170222Sdougb	}
1669170222Sdougb
1670170222Sdougb	if (zonename != NULL) {
1671170222Sdougb		result = dns_message_gettempname(updatemsg, &name);
1672170222Sdougb		check_result(result, "dns_message_gettempname");
1673170222Sdougb		dns_name_init(name, NULL);
1674170222Sdougb		dns_name_clone(zonename, name);
1675170222Sdougb		result = dns_message_gettemprdataset(updatemsg, &rdataset);
1676170222Sdougb		check_result(result, "dns_message_gettemprdataset");
1677170222Sdougb		dns_rdataset_makequestion(rdataset, getzoneclass(),
1678170222Sdougb					  dns_rdatatype_soa);
1679170222Sdougb		ISC_LIST_INIT(name->list);
1680170222Sdougb		ISC_LIST_APPEND(name->list, rdataset, link);
1681170222Sdougb		dns_message_addname(updatemsg, name, DNS_SECTION_ZONE);
1682170222Sdougb	}
1683170222Sdougb}
1684170222Sdougb
1685170222Sdougbstatic void
1686193149Sdougbshow_message(FILE *stream, dns_message_t *msg, const char *description) {
1687135446Strhodes	isc_result_t result;
1688135446Strhodes	isc_buffer_t *buf = NULL;
1689135446Strhodes	int bufsz;
1690135446Strhodes
1691135446Strhodes	ddebug("show_message()");
1692170222Sdougb
1693170222Sdougb	setzone(userzone);
1694170222Sdougb
1695135446Strhodes	bufsz = INITTEXT;
1696186462Sdougb	do {
1697135446Strhodes		if (bufsz > MAXTEXT) {
1698135446Strhodes			fprintf(stderr, "could not allocate large enough "
1699135446Strhodes				"buffer to display message\n");
1700135446Strhodes			exit(1);
1701135446Strhodes		}
1702135446Strhodes		if (buf != NULL)
1703135446Strhodes			isc_buffer_free(&buf);
1704135446Strhodes		result = isc_buffer_allocate(mctx, &buf, bufsz);
1705135446Strhodes		check_result(result, "isc_buffer_allocate");
1706135446Strhodes		result = dns_message_totext(msg, style, 0, buf);
1707135446Strhodes		bufsz *= 2;
1708135446Strhodes	} while (result == ISC_R_NOSPACE);
1709135446Strhodes	if (result != ISC_R_SUCCESS) {
1710135446Strhodes		fprintf(stderr, "could not convert message to text format.\n");
1711135446Strhodes		isc_buffer_free(&buf);
1712135446Strhodes		return;
1713135446Strhodes	}
1714193149Sdougb	fprintf(stream, "%s\n%.*s", description,
1715193149Sdougb	       (int)isc_buffer_usedlength(buf), (char*)isc_buffer_base(buf));
1716135446Strhodes	isc_buffer_free(&buf);
1717135446Strhodes}
1718135446Strhodes
1719135446Strhodes
1720135446Strhodesstatic isc_uint16_t
1721135446Strhodesget_next_command(void) {
1722135446Strhodes	char cmdlinebuf[MAXCMD];
1723135446Strhodes	char *cmdline;
1724135446Strhodes	char *word;
1725135446Strhodes
1726135446Strhodes	ddebug("get_next_command()");
1727165071Sdougb	if (interactive) {
1728135446Strhodes		fprintf(stdout, "> ");
1729165071Sdougb		fflush(stdout);
1730165071Sdougb	}
1731135446Strhodes	isc_app_block();
1732135446Strhodes	cmdline = fgets(cmdlinebuf, MAXCMD, input);
1733135446Strhodes	isc_app_unblock();
1734135446Strhodes	if (cmdline == NULL)
1735135446Strhodes		return (STATUS_QUIT);
1736135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1737135446Strhodes
1738135446Strhodes	if (feof(input))
1739135446Strhodes		return (STATUS_QUIT);
1740135446Strhodes	if (*word == 0)
1741135446Strhodes		return (STATUS_SEND);
1742135446Strhodes	if (word[0] == ';')
1743135446Strhodes		return (STATUS_MORE);
1744135446Strhodes	if (strcasecmp(word, "quit") == 0)
1745135446Strhodes		return (STATUS_QUIT);
1746135446Strhodes	if (strcasecmp(word, "prereq") == 0)
1747135446Strhodes		return (evaluate_prereq(cmdline));
1748135446Strhodes	if (strcasecmp(word, "update") == 0)
1749135446Strhodes		return (evaluate_update(cmdline));
1750135446Strhodes	if (strcasecmp(word, "server") == 0)
1751135446Strhodes		return (evaluate_server(cmdline));
1752135446Strhodes	if (strcasecmp(word, "local") == 0)
1753135446Strhodes		return (evaluate_local(cmdline));
1754135446Strhodes	if (strcasecmp(word, "zone") == 0)
1755135446Strhodes		return (evaluate_zone(cmdline));
1756135446Strhodes	if (strcasecmp(word, "class") == 0)
1757135446Strhodes		return (evaluate_class(cmdline));
1758135446Strhodes	if (strcasecmp(word, "send") == 0)
1759135446Strhodes		return (STATUS_SEND);
1760193149Sdougb	if (strcasecmp(word, "debug") == 0) {
1761193149Sdougb		if (debugging)
1762193149Sdougb			ddebugging = ISC_TRUE;
1763193149Sdougb		else
1764193149Sdougb			debugging = ISC_TRUE;
1765193149Sdougb		return (STATUS_MORE);
1766193149Sdougb	}
1767193149Sdougb	if (strcasecmp(word, "ttl") == 0)
1768193149Sdougb		return (evaluate_ttl(cmdline));
1769135446Strhodes	if (strcasecmp(word, "show") == 0) {
1770193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
1771135446Strhodes		return (STATUS_MORE);
1772135446Strhodes	}
1773135446Strhodes	if (strcasecmp(word, "answer") == 0) {
1774135446Strhodes		if (answer != NULL)
1775193149Sdougb			show_message(stdout, answer, "Answer:");
1776135446Strhodes		return (STATUS_MORE);
1777135446Strhodes	}
1778193149Sdougb	if (strcasecmp(word, "key") == 0) {
1779193149Sdougb		usegsstsig = ISC_FALSE;
1780135446Strhodes		return (evaluate_key(cmdline));
1781193149Sdougb	}
1782193149Sdougb	if (strcasecmp(word, "gsstsig") == 0) {
1783193149Sdougb#ifdef GSSAPI
1784193149Sdougb		usegsstsig = ISC_TRUE;
1785193149Sdougb		use_win2k_gsstsig = ISC_FALSE;
1786193149Sdougb#else
1787193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1788193149Sdougb#endif
1789193149Sdougb		return (STATUS_MORE);
1790193149Sdougb	}
1791193149Sdougb	if (strcasecmp(word, "oldgsstsig") == 0) {
1792193149Sdougb#ifdef GSSAPI
1793193149Sdougb		usegsstsig = ISC_TRUE;
1794193149Sdougb		use_win2k_gsstsig = ISC_TRUE;
1795193149Sdougb#else
1796193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1797193149Sdougb#endif
1798193149Sdougb		return (STATUS_MORE);
1799193149Sdougb	}
1800193149Sdougb	if (strcasecmp(word, "help") == 0) {
1801193149Sdougb		fprintf(stdout,
1802193149Sdougb"local address [port]      (set local resolver)\n"
1803193149Sdougb"server address [port]     (set master server for zone)\n"
1804193149Sdougb"send                      (send the update request)\n"
1805193149Sdougb"show                      (show the update request)\n"
1806193149Sdougb"answer	                   (show the answer to the last request)\n"
1807193149Sdougb"quit                      (quit, any pending update is not sent\n"
1808193149Sdougb"help			   (display this message_\n"
1809193149Sdougb"key [hmac:]keyname secret (use TSIG to sign the request)\n"
1810193149Sdougb"gsstsig                   (use GSS_TSIG to sign the request)\n"
1811193149Sdougb"oldgsstsig                (use Microsoft's GSS_TSIG to sign the request)\n"
1812193149Sdougb"zone name                 (set the zone to be updated)\n"
1813193149Sdougb"class CLASS               (set the zone's DNS class, e.g. IN (default), CH)\n"
1814193149Sdougb"prereq nxdomain name      (does this name not exist)\n"
1815193149Sdougb"prereq yxdomain name      (does this name exist)\n"
1816193149Sdougb"prereq nxrrset ....       (does this RRset exist)\n"
1817193149Sdougb"prereq yxrrset ....       (does this RRset not exist)\n"
1818193149Sdougb"update add ....           (add the given record to the zone)\n"
1819193149Sdougb"update delete ....        (remove the given record(s) from the zone)\n");
1820193149Sdougb		return (STATUS_MORE);
1821193149Sdougb	}
1822135446Strhodes	fprintf(stderr, "incorrect section name: %s\n", word);
1823135446Strhodes	return (STATUS_SYNTAX);
1824135446Strhodes}
1825135446Strhodes
1826135446Strhodesstatic isc_boolean_t
1827135446Strhodesuser_interaction(void) {
1828135446Strhodes	isc_uint16_t result = STATUS_MORE;
1829135446Strhodes
1830135446Strhodes	ddebug("user_interaction()");
1831174187Sdougb	while ((result == STATUS_MORE) || (result == STATUS_SYNTAX)) {
1832135446Strhodes		result = get_next_command();
1833174187Sdougb		if (!interactive && result == STATUS_SYNTAX)
1834174187Sdougb			fatal("syntax error");
1835174187Sdougb	}
1836135446Strhodes	if (result == STATUS_SEND)
1837135446Strhodes		return (ISC_TRUE);
1838135446Strhodes	return (ISC_FALSE);
1839135446Strhodes
1840135446Strhodes}
1841135446Strhodes
1842135446Strhodesstatic void
1843135446Strhodesdone_update(void) {
1844135446Strhodes	isc_event_t *event = global_event;
1845135446Strhodes	ddebug("done_update()");
1846135446Strhodes	isc_task_send(global_task, &event);
1847135446Strhodes}
1848135446Strhodes
1849135446Strhodesstatic void
1850135446Strhodescheck_tsig_error(dns_rdataset_t *rdataset, isc_buffer_t *b) {
1851135446Strhodes	isc_result_t result;
1852135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1853135446Strhodes	dns_rdata_any_tsig_t tsig;
1854135446Strhodes
1855135446Strhodes	result = dns_rdataset_first(rdataset);
1856135446Strhodes	check_result(result, "dns_rdataset_first");
1857135446Strhodes	dns_rdataset_current(rdataset, &rdata);
1858135446Strhodes	result = dns_rdata_tostruct(&rdata, &tsig, NULL);
1859135446Strhodes	check_result(result, "dns_rdata_tostruct");
1860135446Strhodes	if (tsig.error != 0) {
1861135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
1862135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
1863135446Strhodes		isc__buffer_putstr(b, "(" /*)*/);
1864135446Strhodes		result = dns_tsigrcode_totext(tsig.error, b);
1865135446Strhodes		check_result(result, "dns_tsigrcode_totext");
1866135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
1867135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
1868135446Strhodes		isc__buffer_putstr(b,  /*(*/ ")");
1869135446Strhodes	}
1870135446Strhodes}
1871135446Strhodes
1872135446Strhodesstatic void
1873135446Strhodesupdate_completed(isc_task_t *task, isc_event_t *event) {
1874135446Strhodes	dns_requestevent_t *reqev = NULL;
1875135446Strhodes	isc_result_t result;
1876135446Strhodes	dns_request_t *request;
1877135446Strhodes
1878135446Strhodes	UNUSED(task);
1879135446Strhodes
1880135446Strhodes	ddebug("update_completed()");
1881135446Strhodes
1882135446Strhodes	requests--;
1883135446Strhodes
1884135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
1885135446Strhodes	reqev = (dns_requestevent_t *)event;
1886135446Strhodes	request = reqev->request;
1887135446Strhodes
1888135446Strhodes	if (shuttingdown) {
1889135446Strhodes		dns_request_destroy(&request);
1890135446Strhodes		isc_event_free(&event);
1891135446Strhodes		maybeshutdown();
1892135446Strhodes		return;
1893135446Strhodes	}
1894135446Strhodes
1895135446Strhodes	if (reqev->result != ISC_R_SUCCESS) {
1896135446Strhodes		fprintf(stderr, "; Communication with server failed: %s\n",
1897135446Strhodes			isc_result_totext(reqev->result));
1898135446Strhodes		seenerror = ISC_TRUE;
1899135446Strhodes		goto done;
1900135446Strhodes	}
1901135446Strhodes
1902135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &answer);
1903135446Strhodes	check_result(result, "dns_message_create");
1904135446Strhodes	result = dns_request_getresponse(request, answer,
1905135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
1906135446Strhodes	switch (result) {
1907135446Strhodes	case ISC_R_SUCCESS:
1908193149Sdougb		if (answer->verify_attempted)
1909193149Sdougb			ddebug("tsig verification successful");
1910135446Strhodes		break;
1911135446Strhodes	case DNS_R_CLOCKSKEW:
1912135446Strhodes	case DNS_R_EXPECTEDTSIG:
1913135446Strhodes	case DNS_R_TSIGERRORSET:
1914135446Strhodes	case DNS_R_TSIGVERIFYFAILURE:
1915135446Strhodes	case DNS_R_UNEXPECTEDTSIG:
1916193149Sdougb	case ISC_R_FAILURE:
1917193149Sdougb#if 0
1918193149Sdougb		if (usegsstsig && answer->rcode == dns_rcode_noerror) {
1919193149Sdougb			/*
1920193149Sdougb			 * For MS DNS that violates RFC 2845, section 4.2
1921193149Sdougb			 */
1922193149Sdougb			break;
1923193149Sdougb		}
1924193149Sdougb#endif
1925135446Strhodes		fprintf(stderr, "; TSIG error with server: %s\n",
1926135446Strhodes			isc_result_totext(result));
1927135446Strhodes		seenerror = ISC_TRUE;
1928135446Strhodes		break;
1929135446Strhodes	default:
1930135446Strhodes		check_result(result, "dns_request_getresponse");
1931135446Strhodes	}
1932135446Strhodes
1933135446Strhodes	if (answer->rcode != dns_rcode_noerror) {
1934135446Strhodes		seenerror = ISC_TRUE;
1935135446Strhodes		if (!debugging) {
1936135446Strhodes			char buf[64];
1937135446Strhodes			isc_buffer_t b;
1938135446Strhodes			dns_rdataset_t *rds;
1939186462Sdougb
1940135446Strhodes			isc_buffer_init(&b, buf, sizeof(buf) - 1);
1941135446Strhodes			result = dns_rcode_totext(answer->rcode, &b);
1942135446Strhodes			check_result(result, "dns_rcode_totext");
1943135446Strhodes			rds = dns_message_gettsig(answer, NULL);
1944135446Strhodes			if (rds != NULL)
1945135446Strhodes				check_tsig_error(rds, &b);
1946135446Strhodes			fprintf(stderr, "update failed: %.*s\n",
1947135446Strhodes				(int)isc_buffer_usedlength(&b), buf);
1948135446Strhodes		}
1949135446Strhodes	}
1950193149Sdougb	if (debugging)
1951193149Sdougb		show_message(stderr, answer, "\nReply from update query:");
1952135446Strhodes
1953135446Strhodes done:
1954135446Strhodes	dns_request_destroy(&request);
1955193149Sdougb	if (usegsstsig) {
1956193149Sdougb		dns_name_free(&tmpzonename, mctx);
1957193149Sdougb		dns_name_free(&restart_master, mctx);
1958193149Sdougb	}
1959135446Strhodes	isc_event_free(&event);
1960135446Strhodes	done_update();
1961135446Strhodes}
1962135446Strhodes
1963135446Strhodesstatic void
1964135446Strhodessend_update(dns_name_t *zonename, isc_sockaddr_t *master,
1965135446Strhodes	    isc_sockaddr_t *srcaddr)
1966135446Strhodes{
1967135446Strhodes	isc_result_t result;
1968135446Strhodes	dns_request_t *request = NULL;
1969135446Strhodes	unsigned int options = 0;
1970135446Strhodes
1971135446Strhodes	ddebug("send_update()");
1972135446Strhodes
1973170222Sdougb	setzone(zonename);
1974135446Strhodes
1975135446Strhodes	if (usevc)
1976135446Strhodes		options |= DNS_REQUESTOPT_TCP;
1977135446Strhodes	if (tsigkey == NULL && sig0key != NULL) {
1978135446Strhodes		result = dns_message_setsig0key(updatemsg, sig0key);
1979135446Strhodes		check_result(result, "dns_message_setsig0key");
1980135446Strhodes	}
1981135446Strhodes	if (debugging) {
1982135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
1983135446Strhodes
1984135446Strhodes		isc_sockaddr_format(master, addrbuf, sizeof(addrbuf));
1985135446Strhodes		fprintf(stderr, "Sending update to %s\n", addrbuf);
1986135446Strhodes	}
1987193149Sdougb
1988135446Strhodes	result = dns_request_createvia3(requestmgr, updatemsg, srcaddr,
1989135446Strhodes					master, options, tsigkey, timeout,
1990135446Strhodes					udp_timeout, udp_retries, global_task,
1991135446Strhodes					update_completed, NULL, &request);
1992135446Strhodes	check_result(result, "dns_request_createvia3");
1993135446Strhodes
1994135446Strhodes	if (debugging)
1995193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
1996135446Strhodes
1997135446Strhodes	requests++;
1998135446Strhodes}
1999135446Strhodes
2000135446Strhodesstatic void
2001135446Strhodesrecvsoa(isc_task_t *task, isc_event_t *event) {
2002135446Strhodes	dns_requestevent_t *reqev = NULL;
2003135446Strhodes	dns_request_t *request = NULL;
2004135446Strhodes	isc_result_t result, eresult;
2005135446Strhodes	dns_message_t *rcvmsg = NULL;
2006135446Strhodes	dns_section_t section;
2007135446Strhodes	dns_name_t *name = NULL;
2008135446Strhodes	dns_rdataset_t *soaset = NULL;
2009135446Strhodes	dns_rdata_soa_t soa;
2010135446Strhodes	dns_rdata_t soarr = DNS_RDATA_INIT;
2011135446Strhodes	int pass = 0;
2012135446Strhodes	dns_name_t master;
2013135446Strhodes	nsu_requestinfo_t *reqinfo;
2014135446Strhodes	dns_message_t *soaquery = NULL;
2015135446Strhodes	isc_sockaddr_t *addr;
2016135446Strhodes	isc_boolean_t seencname = ISC_FALSE;
2017143731Sdougb	dns_name_t tname;
2018143731Sdougb	unsigned int nlabels;
2019135446Strhodes
2020135446Strhodes	UNUSED(task);
2021135446Strhodes
2022135446Strhodes	ddebug("recvsoa()");
2023135446Strhodes
2024135446Strhodes	requests--;
2025186462Sdougb
2026135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2027135446Strhodes	reqev = (dns_requestevent_t *)event;
2028135446Strhodes	request = reqev->request;
2029135446Strhodes	eresult = reqev->result;
2030135446Strhodes	reqinfo = reqev->ev_arg;
2031135446Strhodes	soaquery = reqinfo->msg;
2032135446Strhodes	addr = reqinfo->addr;
2033135446Strhodes
2034135446Strhodes	if (shuttingdown) {
2035135446Strhodes		dns_request_destroy(&request);
2036135446Strhodes		dns_message_destroy(&soaquery);
2037135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2038135446Strhodes		isc_event_free(&event);
2039135446Strhodes		maybeshutdown();
2040135446Strhodes		return;
2041135446Strhodes	}
2042135446Strhodes
2043135446Strhodes	if (eresult != ISC_R_SUCCESS) {
2044135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2045135446Strhodes
2046135446Strhodes		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2047135446Strhodes		fprintf(stderr, "; Communication with %s failed: %s\n",
2048193149Sdougb			addrbuf, isc_result_totext(eresult));
2049135446Strhodes		if (userserver != NULL)
2050135446Strhodes			fatal("could not talk to specified name server");
2051135446Strhodes		else if (++ns_inuse >= lwconf->nsnext)
2052135446Strhodes			fatal("could not talk to any default name server");
2053135446Strhodes		ddebug("Destroying request [%p]", request);
2054135446Strhodes		dns_request_destroy(&request);
2055135446Strhodes		dns_message_renderreset(soaquery);
2056153816Sdougb		dns_message_settsigkey(soaquery, NULL);
2057135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2058135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2059135446Strhodes		isc_event_free(&event);
2060135446Strhodes		setzoneclass(dns_rdataclass_none);
2061135446Strhodes		return;
2062135446Strhodes	}
2063170222Sdougb
2064135446Strhodes	isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2065170222Sdougb	reqinfo = NULL;
2066135446Strhodes	isc_event_free(&event);
2067135446Strhodes	reqev = NULL;
2068135446Strhodes
2069135446Strhodes	ddebug("About to create rcvmsg");
2070135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2071135446Strhodes	check_result(result, "dns_message_create");
2072135446Strhodes	result = dns_request_getresponse(request, rcvmsg,
2073135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
2074135446Strhodes	if (result == DNS_R_TSIGERRORSET && userserver != NULL) {
2075135446Strhodes		dns_message_destroy(&rcvmsg);
2076135446Strhodes		ddebug("Destroying request [%p]", request);
2077135446Strhodes		dns_request_destroy(&request);
2078135446Strhodes		reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2079135446Strhodes		if (reqinfo == NULL)
2080135446Strhodes			fatal("out of memory");
2081135446Strhodes		reqinfo->msg = soaquery;
2082135446Strhodes		reqinfo->addr = addr;
2083135446Strhodes		dns_message_renderreset(soaquery);
2084135446Strhodes		ddebug("retrying soa request without TSIG");
2085135446Strhodes		result = dns_request_createvia3(requestmgr, soaquery,
2086135446Strhodes						localaddr, addr, 0, NULL,
2087135446Strhodes						FIND_TIMEOUT * 20,
2088165071Sdougb						FIND_TIMEOUT, 3,
2089135446Strhodes						global_task, recvsoa, reqinfo,
2090135446Strhodes						&request);
2091135446Strhodes		check_result(result, "dns_request_createvia");
2092135446Strhodes		requests++;
2093135446Strhodes		return;
2094135446Strhodes	}
2095135446Strhodes	check_result(result, "dns_request_getresponse");
2096135446Strhodes	section = DNS_SECTION_ANSWER;
2097193149Sdougb	if (debugging)
2098193149Sdougb		show_message(stderr, rcvmsg, "Reply from SOA query:");
2099135446Strhodes
2100135446Strhodes	if (rcvmsg->rcode != dns_rcode_noerror &&
2101135446Strhodes	    rcvmsg->rcode != dns_rcode_nxdomain)
2102135446Strhodes		fatal("response to SOA query was unsuccessful");
2103135446Strhodes
2104170222Sdougb	if (userzone != NULL && rcvmsg->rcode == dns_rcode_nxdomain) {
2105170222Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
2106170222Sdougb		dns_name_format(userzone, namebuf, sizeof(namebuf));
2107170222Sdougb		error("specified zone '%s' does not exist (NXDOMAIN)",
2108170222Sdougb		      namebuf);
2109170222Sdougb		dns_message_destroy(&rcvmsg);
2110170222Sdougb		dns_request_destroy(&request);
2111170222Sdougb		dns_message_destroy(&soaquery);
2112170222Sdougb		ddebug("Out of recvsoa");
2113170222Sdougb		done_update();
2114170222Sdougb		return;
2115170222Sdougb	}
2116170222Sdougb
2117135446Strhodes lookforsoa:
2118135446Strhodes	if (pass == 0)
2119135446Strhodes		section = DNS_SECTION_ANSWER;
2120135446Strhodes	else if (pass == 1)
2121135446Strhodes		section = DNS_SECTION_AUTHORITY;
2122186462Sdougb	else
2123143731Sdougb		goto droplabel;
2124135446Strhodes
2125135446Strhodes	result = dns_message_firstname(rcvmsg, section);
2126135446Strhodes	if (result != ISC_R_SUCCESS) {
2127135446Strhodes		pass++;
2128135446Strhodes		goto lookforsoa;
2129135446Strhodes	}
2130135446Strhodes	while (result == ISC_R_SUCCESS) {
2131135446Strhodes		name = NULL;
2132135446Strhodes		dns_message_currentname(rcvmsg, section, &name);
2133135446Strhodes		soaset = NULL;
2134135446Strhodes		result = dns_message_findtype(name, dns_rdatatype_soa, 0,
2135135446Strhodes					      &soaset);
2136135446Strhodes		if (result == ISC_R_SUCCESS)
2137135446Strhodes			break;
2138135446Strhodes		if (section == DNS_SECTION_ANSWER) {
2139135446Strhodes			dns_rdataset_t *tset = NULL;
2140135446Strhodes			if (dns_message_findtype(name, dns_rdatatype_cname, 0,
2141193149Sdougb						 &tset) == ISC_R_SUCCESS ||
2142135446Strhodes			    dns_message_findtype(name, dns_rdatatype_dname, 0,
2143193149Sdougb						 &tset) == ISC_R_SUCCESS ) {
2144135446Strhodes				seencname = ISC_TRUE;
2145135446Strhodes				break;
2146135446Strhodes			}
2147135446Strhodes		}
2148186462Sdougb
2149135446Strhodes		result = dns_message_nextname(rcvmsg, section);
2150135446Strhodes	}
2151135446Strhodes
2152135446Strhodes	if (soaset == NULL && !seencname) {
2153135446Strhodes		pass++;
2154135446Strhodes		goto lookforsoa;
2155135446Strhodes	}
2156135446Strhodes
2157143731Sdougb	if (seencname)
2158143731Sdougb		goto droplabel;
2159135446Strhodes
2160135446Strhodes	if (debugging) {
2161135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2162135446Strhodes		dns_name_format(name, namestr, sizeof(namestr));
2163135446Strhodes		fprintf(stderr, "Found zone name: %s\n", namestr);
2164135446Strhodes	}
2165135446Strhodes
2166135446Strhodes	result = dns_rdataset_first(soaset);
2167135446Strhodes	check_result(result, "dns_rdataset_first");
2168135446Strhodes
2169135446Strhodes	dns_rdata_init(&soarr);
2170135446Strhodes	dns_rdataset_current(soaset, &soarr);
2171135446Strhodes	result = dns_rdata_tostruct(&soarr, &soa, NULL);
2172135446Strhodes	check_result(result, "dns_rdata_tostruct");
2173135446Strhodes
2174135446Strhodes	dns_name_init(&master, NULL);
2175135446Strhodes	dns_name_clone(&soa.origin, &master);
2176135446Strhodes
2177135446Strhodes	if (userzone != NULL)
2178135446Strhodes		zonename = userzone;
2179135446Strhodes	else
2180135446Strhodes		zonename = name;
2181135446Strhodes
2182135446Strhodes	if (debugging) {
2183135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2184135446Strhodes		dns_name_format(&master, namestr, sizeof(namestr));
2185135446Strhodes		fprintf(stderr, "The master is: %s\n", namestr);
2186135446Strhodes	}
2187135446Strhodes
2188135446Strhodes	if (userserver != NULL)
2189135446Strhodes		serveraddr = userserver;
2190135446Strhodes	else {
2191135446Strhodes		char serverstr[DNS_NAME_MAXTEXT+1];
2192135446Strhodes		isc_buffer_t buf;
2193135446Strhodes
2194135446Strhodes		isc_buffer_init(&buf, serverstr, sizeof(serverstr));
2195135446Strhodes		result = dns_name_totext(&master, ISC_TRUE, &buf);
2196135446Strhodes		check_result(result, "dns_name_totext");
2197135446Strhodes		serverstr[isc_buffer_usedlength(&buf)] = 0;
2198135446Strhodes		get_address(serverstr, DNSDEFAULTPORT, &tempaddr);
2199135446Strhodes		serveraddr = &tempaddr;
2200135446Strhodes	}
2201143731Sdougb	dns_rdata_freestruct(&soa);
2202135446Strhodes
2203193149Sdougb#ifdef GSSAPI
2204193149Sdougb	if (usegsstsig) {
2205193149Sdougb		dns_name_init(&tmpzonename, NULL);
2206193149Sdougb		dns_name_dup(zonename, mctx, &tmpzonename);
2207193149Sdougb		dns_name_init(&restart_master, NULL);
2208193149Sdougb		dns_name_dup(&master, mctx, &restart_master);
2209193149Sdougb		start_gssrequest(&master);
2210193149Sdougb	} else {
2211193149Sdougb		send_update(zonename, serveraddr, localaddr);
2212193149Sdougb		setzoneclass(dns_rdataclass_none);
2213193149Sdougb	}
2214193149Sdougb#else
2215135446Strhodes	send_update(zonename, serveraddr, localaddr);
2216143731Sdougb	setzoneclass(dns_rdataclass_none);
2217193149Sdougb#endif
2218135446Strhodes
2219135446Strhodes	dns_message_destroy(&soaquery);
2220135446Strhodes	dns_request_destroy(&request);
2221135446Strhodes
2222135446Strhodes out:
2223135446Strhodes	dns_message_destroy(&rcvmsg);
2224135446Strhodes	ddebug("Out of recvsoa");
2225143731Sdougb	return;
2226186462Sdougb
2227143731Sdougb droplabel:
2228143731Sdougb	result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
2229143731Sdougb	INSIST(result == ISC_R_SUCCESS);
2230143731Sdougb	name = NULL;
2231143731Sdougb	dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
2232143731Sdougb	nlabels = dns_name_countlabels(name);
2233143731Sdougb	if (nlabels == 1)
2234143731Sdougb		fatal("could not find enclosing zone");
2235143731Sdougb	dns_name_init(&tname, NULL);
2236143731Sdougb	dns_name_getlabelsequence(name, 1, nlabels - 1, &tname);
2237143731Sdougb	dns_name_clone(&tname, name);
2238143731Sdougb	dns_request_destroy(&request);
2239143731Sdougb	dns_message_renderreset(soaquery);
2240153816Sdougb	dns_message_settsigkey(soaquery, NULL);
2241143731Sdougb	if (userserver != NULL)
2242143731Sdougb		sendrequest(localaddr, userserver, soaquery, &request);
2243143731Sdougb	else
2244193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2245143731Sdougb	goto out;
2246135446Strhodes}
2247135446Strhodes
2248135446Strhodesstatic void
2249135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2250135446Strhodes	    dns_message_t *msg, dns_request_t **request)
2251135446Strhodes{
2252135446Strhodes	isc_result_t result;
2253135446Strhodes	nsu_requestinfo_t *reqinfo;
2254135446Strhodes
2255135446Strhodes	reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2256135446Strhodes	if (reqinfo == NULL)
2257135446Strhodes		fatal("out of memory");
2258135446Strhodes	reqinfo->msg = msg;
2259135446Strhodes	reqinfo->addr = destaddr;
2260135446Strhodes	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr, 0,
2261135446Strhodes					(userserver != NULL) ? tsigkey : NULL,
2262135446Strhodes					FIND_TIMEOUT * 20, FIND_TIMEOUT, 3,
2263135446Strhodes					global_task, recvsoa, reqinfo, request);
2264135446Strhodes	check_result(result, "dns_request_createvia");
2265135446Strhodes	requests++;
2266135446Strhodes}
2267135446Strhodes
2268193149Sdougb#ifdef GSSAPI
2269135446Strhodesstatic void
2270193149Sdougbstart_gssrequest(dns_name_t *master)
2271193149Sdougb{
2272193149Sdougb	gss_ctx_id_t context;
2273193149Sdougb	isc_buffer_t buf;
2274193149Sdougb	isc_result_t result;
2275193149Sdougb	isc_uint32_t val = 0;
2276193149Sdougb	dns_message_t *rmsg;
2277193149Sdougb	dns_request_t *request = NULL;
2278193149Sdougb	dns_name_t *servname;
2279193149Sdougb	dns_fixedname_t fname;
2280193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
2281193149Sdougb	char keystr[DNS_NAME_FORMATSIZE];
2282193149Sdougb
2283193149Sdougb	debug("start_gssrequest");
2284193149Sdougb	usevc = ISC_TRUE;
2285193149Sdougb
2286193149Sdougb	if (gssring != NULL)
2287193149Sdougb		dns_tsigkeyring_destroy(&gssring);
2288193149Sdougb	gssring = NULL;
2289193149Sdougb	result = dns_tsigkeyring_create(mctx, &gssring);
2290193149Sdougb
2291193149Sdougb	if (result != ISC_R_SUCCESS)
2292193149Sdougb		fatal("dns_tsigkeyring_create failed: %s",
2293193149Sdougb		      isc_result_totext(result));
2294193149Sdougb
2295193149Sdougb	dns_name_format(master, namestr, sizeof(namestr));
2296193149Sdougb	if (kserver == NULL) {
2297193149Sdougb		kserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
2298193149Sdougb		if (kserver == NULL)
2299193149Sdougb			fatal("out of memory");
2300193149Sdougb	}
2301193149Sdougb	if (userserver == NULL)
2302193149Sdougb		get_address(namestr, DNSDEFAULTPORT, kserver);
2303193149Sdougb	else
2304193149Sdougb		(void)memcpy(kserver, userserver, sizeof(isc_sockaddr_t));
2305193149Sdougb
2306193149Sdougb	dns_fixedname_init(&fname);
2307193149Sdougb	servname = dns_fixedname_name(&fname);
2308193149Sdougb
2309193149Sdougb	result = isc_string_printf(servicename, sizeof(servicename),
2310193149Sdougb				   "DNS/%s", namestr);
2311193149Sdougb	if (result != ISC_R_SUCCESS)
2312193149Sdougb		fatal("isc_string_printf(servicename) failed: %s",
2313193149Sdougb		      isc_result_totext(result));
2314193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2315193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2316193149Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname,
2317193149Sdougb				   ISC_FALSE, NULL);
2318193149Sdougb	if (result != ISC_R_SUCCESS)
2319193149Sdougb		fatal("dns_name_fromtext(servname) failed: %s",
2320193149Sdougb		      isc_result_totext(result));
2321193149Sdougb
2322193149Sdougb	dns_fixedname_init(&fkname);
2323193149Sdougb	keyname = dns_fixedname_name(&fkname);
2324193149Sdougb
2325193149Sdougb	isc_random_get(&val);
2326193149Sdougb	result = isc_string_printf(keystr, sizeof(keystr), "%u.sig-%s",
2327193149Sdougb				   val, namestr);
2328193149Sdougb	if (result != ISC_R_SUCCESS)
2329193149Sdougb		fatal("isc_string_printf(keystr) failed: %s",
2330193149Sdougb		      isc_result_totext(result));
2331193149Sdougb	isc_buffer_init(&buf, keystr, strlen(keystr));
2332193149Sdougb	isc_buffer_add(&buf, strlen(keystr));
2333193149Sdougb
2334193149Sdougb	result = dns_name_fromtext(keyname, &buf, dns_rootname,
2335193149Sdougb				   ISC_FALSE, NULL);
2336193149Sdougb	if (result != ISC_R_SUCCESS)
2337193149Sdougb		fatal("dns_name_fromtext(keyname) failed: %s",
2338193149Sdougb		      isc_result_totext(result));
2339193149Sdougb
2340193149Sdougb	/* Windows doesn't recognize name compression in the key name. */
2341193149Sdougb	keyname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2342193149Sdougb
2343193149Sdougb	rmsg = NULL;
2344193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &rmsg);
2345193149Sdougb	if (result != ISC_R_SUCCESS)
2346193149Sdougb		fatal("dns_message_create failed: %s",
2347193149Sdougb		      isc_result_totext(result));
2348193149Sdougb
2349193149Sdougb	/* Build first request. */
2350193149Sdougb
2351193149Sdougb	context = GSS_C_NO_CONTEXT;
2352193149Sdougb	result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0,
2353193149Sdougb					&context, use_win2k_gsstsig);
2354193149Sdougb	if (result == ISC_R_FAILURE)
2355193149Sdougb		fatal("Check your Kerberos ticket, it may have expired.");
2356193149Sdougb	if (result != ISC_R_SUCCESS)
2357193149Sdougb		fatal("dns_tkey_buildgssquery failed: %s",
2358193149Sdougb		      isc_result_totext(result));
2359193149Sdougb
2360193149Sdougb	send_gssrequest(localaddr, kserver, rmsg, &request, context);
2361193149Sdougb}
2362193149Sdougb
2363193149Sdougbstatic void
2364193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2365193149Sdougb		dns_message_t *msg, dns_request_t **request,
2366193149Sdougb		gss_ctx_id_t context)
2367193149Sdougb{
2368193149Sdougb	isc_result_t result;
2369193149Sdougb	nsu_gssinfo_t *reqinfo;
2370193149Sdougb	unsigned int options = 0;
2371193149Sdougb
2372193149Sdougb	debug("send_gssrequest");
2373193149Sdougb	reqinfo = isc_mem_get(mctx, sizeof(nsu_gssinfo_t));
2374193149Sdougb	if (reqinfo == NULL)
2375193149Sdougb		fatal("out of memory");
2376193149Sdougb	reqinfo->msg = msg;
2377193149Sdougb	reqinfo->addr = destaddr;
2378193149Sdougb	reqinfo->context = context;
2379193149Sdougb
2380193149Sdougb	options |= DNS_REQUESTOPT_TCP;
2381193149Sdougb	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr,
2382193149Sdougb					options, tsigkey, FIND_TIMEOUT * 20,
2383193149Sdougb					FIND_TIMEOUT, 3, global_task, recvgss,
2384193149Sdougb					reqinfo, request);
2385193149Sdougb	check_result(result, "dns_request_createvia3");
2386193149Sdougb	if (debugging)
2387193149Sdougb		show_message(stdout, msg, "Outgoing update query:");
2388193149Sdougb	requests++;
2389193149Sdougb}
2390193149Sdougb
2391193149Sdougbstatic void
2392193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event) {
2393193149Sdougb	dns_requestevent_t *reqev = NULL;
2394193149Sdougb	dns_request_t *request = NULL;
2395193149Sdougb	isc_result_t result, eresult;
2396193149Sdougb	dns_message_t *rcvmsg = NULL;
2397193149Sdougb	nsu_gssinfo_t *reqinfo;
2398193149Sdougb	dns_message_t *tsigquery = NULL;
2399193149Sdougb	isc_sockaddr_t *addr;
2400193149Sdougb	gss_ctx_id_t context;
2401193149Sdougb	isc_buffer_t buf;
2402193149Sdougb	dns_name_t *servname;
2403193149Sdougb	dns_fixedname_t fname;
2404193149Sdougb
2405193149Sdougb	UNUSED(task);
2406193149Sdougb
2407193149Sdougb	ddebug("recvgss()");
2408193149Sdougb
2409193149Sdougb	requests--;
2410193149Sdougb
2411193149Sdougb	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2412193149Sdougb	reqev = (dns_requestevent_t *)event;
2413193149Sdougb	request = reqev->request;
2414193149Sdougb	eresult = reqev->result;
2415193149Sdougb	reqinfo = reqev->ev_arg;
2416193149Sdougb	tsigquery = reqinfo->msg;
2417193149Sdougb	context = reqinfo->context;
2418193149Sdougb	addr = reqinfo->addr;
2419193149Sdougb
2420193149Sdougb	if (shuttingdown) {
2421193149Sdougb		dns_request_destroy(&request);
2422193149Sdougb		dns_message_destroy(&tsigquery);
2423193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2424193149Sdougb		isc_event_free(&event);
2425193149Sdougb		maybeshutdown();
2426193149Sdougb		return;
2427193149Sdougb	}
2428193149Sdougb
2429193149Sdougb	if (eresult != ISC_R_SUCCESS) {
2430193149Sdougb		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2431193149Sdougb
2432193149Sdougb		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2433193149Sdougb		fprintf(stderr, "; Communication with %s failed: %s\n",
2434193149Sdougb			addrbuf, isc_result_totext(eresult));
2435193149Sdougb		if (userserver != NULL)
2436193149Sdougb			fatal("could not talk to specified name server");
2437193149Sdougb		else if (++ns_inuse >= lwconf->nsnext)
2438193149Sdougb			fatal("could not talk to any default name server");
2439193149Sdougb		ddebug("Destroying request [%p]", request);
2440193149Sdougb		dns_request_destroy(&request);
2441193149Sdougb		dns_message_renderreset(tsigquery);
2442193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], tsigquery,
2443193149Sdougb			    &request);
2444193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2445193149Sdougb		isc_event_free(&event);
2446193149Sdougb		return;
2447193149Sdougb	}
2448193149Sdougb	isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2449193149Sdougb
2450193149Sdougb	isc_event_free(&event);
2451193149Sdougb	reqev = NULL;
2452193149Sdougb
2453193149Sdougb	ddebug("recvgss creating rcvmsg");
2454193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2455193149Sdougb	check_result(result, "dns_message_create");
2456193149Sdougb
2457193149Sdougb	result = dns_request_getresponse(request, rcvmsg,
2458193149Sdougb					 DNS_MESSAGEPARSE_PRESERVEORDER);
2459193149Sdougb	check_result(result, "dns_request_getresponse");
2460193149Sdougb
2461193149Sdougb	if (debugging)
2462193149Sdougb		show_message(stderr, rcvmsg,
2463193149Sdougb			     "recvmsg reply from GSS-TSIG query");
2464193149Sdougb
2465193149Sdougb	if (rcvmsg->rcode == dns_rcode_formerr && !tried_other_gsstsig) {
2466193149Sdougb		ddebug("recvgss trying %s GSS-TSIG",
2467193149Sdougb		       use_win2k_gsstsig ? "Standard" : "Win2k");
2468193149Sdougb		if (use_win2k_gsstsig)
2469193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
2470193149Sdougb		else
2471193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
2472193149Sdougb		tried_other_gsstsig = ISC_TRUE;
2473193149Sdougb		start_gssrequest(&restart_master);
2474193149Sdougb		goto done;
2475193149Sdougb	}
2476193149Sdougb
2477193149Sdougb	if (rcvmsg->rcode != dns_rcode_noerror &&
2478193149Sdougb	    rcvmsg->rcode != dns_rcode_nxdomain)
2479193149Sdougb		fatal("response to GSS-TSIG query was unsuccessful");
2480193149Sdougb
2481193149Sdougb
2482193149Sdougb	dns_fixedname_init(&fname);
2483193149Sdougb	servname = dns_fixedname_name(&fname);
2484193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2485193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2486193149Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname,
2487193149Sdougb				   ISC_FALSE, NULL);
2488193149Sdougb	check_result(result, "dns_name_fromtext");
2489193149Sdougb
2490193149Sdougb	tsigkey = NULL;
2491193149Sdougb	result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname,
2492193149Sdougb				       &context, &tsigkey, gssring,
2493193149Sdougb				       use_win2k_gsstsig);
2494193149Sdougb	switch (result) {
2495193149Sdougb
2496193149Sdougb	case DNS_R_CONTINUE:
2497193149Sdougb		send_gssrequest(localaddr, kserver, tsigquery, &request,
2498193149Sdougb				context);
2499193149Sdougb		break;
2500193149Sdougb
2501193149Sdougb	case ISC_R_SUCCESS:
2502193149Sdougb		/*
2503193149Sdougb		 * XXXSRA Waaay too much fun here.  There's no good
2504193149Sdougb		 * reason why we need a TSIG here (the people who put
2505193149Sdougb		 * it into the spec admitted at the time that it was
2506193149Sdougb		 * not a security issue), and Windows clients don't
2507193149Sdougb		 * seem to work if named complies with the spec and
2508193149Sdougb		 * includes the gratuitous TSIG.  So we're in the
2509193149Sdougb		 * bizarre situation of having to choose between
2510193149Sdougb		 * complying with a useless requirement in the spec
2511193149Sdougb		 * and interoperating.  This is nuts.  If we can
2512193149Sdougb		 * confirm this behavior, we should ask the WG to
2513193149Sdougb		 * consider removing the requirement for the
2514193149Sdougb		 * gratuitous TSIG here.  For the moment, we ignore
2515193149Sdougb		 * the TSIG -- this too is a spec violation, but it's
2516193149Sdougb		 * the least insane thing to do.
2517193149Sdougb		 */
2518193149Sdougb#if 0
2519193149Sdougb		/*
2520193149Sdougb		 * Verify the signature.
2521193149Sdougb		 */
2522193149Sdougb		rcvmsg->state = DNS_SECTION_ANY;
2523193149Sdougb		dns_message_setquerytsig(rcvmsg, NULL);
2524193149Sdougb		result = dns_message_settsigkey(rcvmsg, tsigkey);
2525193149Sdougb		check_result(result, "dns_message_settsigkey");
2526193149Sdougb		result = dns_message_checksig(rcvmsg, NULL);
2527193149Sdougb		ddebug("tsig verification: %s", dns_result_totext(result));
2528193149Sdougb		check_result(result, "dns_message_checksig");
2529193149Sdougb#endif /* 0 */
2530193149Sdougb
2531193149Sdougb		send_update(&tmpzonename, serveraddr, localaddr);
2532193149Sdougb		setzoneclass(dns_rdataclass_none);
2533193149Sdougb		break;
2534193149Sdougb
2535193149Sdougb	default:
2536193149Sdougb		fatal("dns_tkey_negotiategss: %s", isc_result_totext(result));
2537193149Sdougb	}
2538193149Sdougb
2539193149Sdougb done:
2540193149Sdougb	dns_request_destroy(&request);
2541193149Sdougb	dns_message_destroy(&tsigquery);
2542193149Sdougb
2543193149Sdougb	dns_message_destroy(&rcvmsg);
2544193149Sdougb	ddebug("Out of recvgss");
2545193149Sdougb}
2546193149Sdougb#endif
2547193149Sdougb
2548193149Sdougbstatic void
2549135446Strhodesstart_update(void) {
2550135446Strhodes	isc_result_t result;
2551135446Strhodes	dns_rdataset_t *rdataset = NULL;
2552135446Strhodes	dns_name_t *name = NULL;
2553135446Strhodes	dns_request_t *request = NULL;
2554135446Strhodes	dns_message_t *soaquery = NULL;
2555135446Strhodes	dns_name_t *firstname;
2556135446Strhodes	dns_section_t section = DNS_SECTION_UPDATE;
2557135446Strhodes
2558135446Strhodes	ddebug("start_update()");
2559135446Strhodes
2560135446Strhodes	if (answer != NULL)
2561135446Strhodes		dns_message_destroy(&answer);
2562135446Strhodes
2563193149Sdougb	if (userzone != NULL && userserver != NULL && ! usegsstsig) {
2564135446Strhodes		send_update(userzone, userserver, localaddr);
2565135446Strhodes		setzoneclass(dns_rdataclass_none);
2566135446Strhodes		return;
2567135446Strhodes	}
2568135446Strhodes
2569135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
2570135446Strhodes				    &soaquery);
2571135446Strhodes	check_result(result, "dns_message_create");
2572135446Strhodes
2573170222Sdougb	if (userserver == NULL)
2574170222Sdougb		soaquery->flags |= DNS_MESSAGEFLAG_RD;
2575135446Strhodes
2576135446Strhodes	result = dns_message_gettempname(soaquery, &name);
2577135446Strhodes	check_result(result, "dns_message_gettempname");
2578135446Strhodes
2579135446Strhodes	result = dns_message_gettemprdataset(soaquery, &rdataset);
2580135446Strhodes	check_result(result, "dns_message_gettemprdataset");
2581135446Strhodes
2582135446Strhodes	dns_rdataset_makequestion(rdataset, getzoneclass(), dns_rdatatype_soa);
2583135446Strhodes
2584170222Sdougb	if (userzone != NULL) {
2585170222Sdougb		dns_name_init(name, NULL);
2586170222Sdougb		dns_name_clone(userzone, name);
2587170222Sdougb	} else {
2588170222Sdougb		result = dns_message_firstname(updatemsg, section);
2589170222Sdougb		if (result == ISC_R_NOMORE) {
2590170222Sdougb			section = DNS_SECTION_PREREQUISITE;
2591170222Sdougb			result = dns_message_firstname(updatemsg, section);
2592170222Sdougb		}
2593170222Sdougb		if (result != ISC_R_SUCCESS) {
2594174187Sdougb			dns_message_puttempname(soaquery, &name);
2595174187Sdougb			dns_rdataset_disassociate(rdataset);
2596174187Sdougb			dns_message_puttemprdataset(soaquery, &rdataset);
2597174187Sdougb			dns_message_destroy(&soaquery);
2598170222Sdougb			done_update();
2599170222Sdougb			return;
2600170222Sdougb		}
2601170222Sdougb		firstname = NULL;
2602170222Sdougb		dns_message_currentname(updatemsg, section, &firstname);
2603170222Sdougb		dns_name_init(name, NULL);
2604170222Sdougb		dns_name_clone(firstname, name);
2605170222Sdougb	}
2606135446Strhodes
2607135446Strhodes	ISC_LIST_INIT(name->list);
2608135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
2609135446Strhodes	dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
2610135446Strhodes
2611135446Strhodes	if (userserver != NULL)
2612135446Strhodes		sendrequest(localaddr, userserver, soaquery, &request);
2613135446Strhodes	else {
2614135446Strhodes		ns_inuse = 0;
2615135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2616135446Strhodes	}
2617135446Strhodes}
2618135446Strhodes
2619135446Strhodesstatic void
2620135446Strhodescleanup(void) {
2621135446Strhodes	ddebug("cleanup()");
2622135446Strhodes
2623135446Strhodes	if (answer != NULL)
2624135446Strhodes		dns_message_destroy(&answer);
2625193149Sdougb
2626193149Sdougb#ifdef GSSAPI
2627193149Sdougb	if (tsigkey != NULL) {
2628193149Sdougb		ddebug("detach tsigkey x%p", tsigkey);
2629193149Sdougb		dns_tsigkey_detach(&tsigkey);
2630193149Sdougb	}
2631193149Sdougb	if (gssring != NULL) {
2632193149Sdougb		ddebug("Destroying GSS-TSIG keyring");
2633193149Sdougb		dns_tsigkeyring_destroy(&gssring);
2634193149Sdougb	}
2635193149Sdougb	if (kserver != NULL) {
2636193149Sdougb		isc_mem_put(mctx, kserver, sizeof(isc_sockaddr_t));
2637193149Sdougb		kserver = NULL;
2638193149Sdougb	}
2639193149Sdougb#endif
2640193149Sdougb
2641135446Strhodes	ddebug("Shutting down task manager");
2642135446Strhodes	isc_taskmgr_destroy(&taskmgr);
2643135446Strhodes
2644135446Strhodes	ddebug("Destroying event");
2645135446Strhodes	isc_event_free(&global_event);
2646135446Strhodes
2647135446Strhodes	ddebug("Shutting down socket manager");
2648135446Strhodes	isc_socketmgr_destroy(&socketmgr);
2649135446Strhodes
2650135446Strhodes	ddebug("Shutting down timer manager");
2651135446Strhodes	isc_timermgr_destroy(&timermgr);
2652135446Strhodes
2653135446Strhodes	ddebug("Destroying hash context");
2654135446Strhodes	isc_hash_destroy();
2655135446Strhodes
2656170222Sdougb	ddebug("Destroying name state");
2657170222Sdougb	dns_name_destroy();
2658170222Sdougb
2659193149Sdougb	ddebug("Removing log context");
2660193149Sdougb	isc_log_destroy(&lctx);
2661193149Sdougb
2662135446Strhodes	ddebug("Destroying memory context");
2663135446Strhodes	if (memdebugging)
2664135446Strhodes		isc_mem_stats(mctx, stderr);
2665135446Strhodes	isc_mem_destroy(&mctx);
2666135446Strhodes}
2667135446Strhodes
2668135446Strhodesstatic void
2669135446Strhodesgetinput(isc_task_t *task, isc_event_t *event) {
2670135446Strhodes	isc_boolean_t more;
2671135446Strhodes
2672135446Strhodes	UNUSED(task);
2673135446Strhodes
2674135446Strhodes	if (shuttingdown) {
2675135446Strhodes		maybeshutdown();
2676135446Strhodes		return;
2677135446Strhodes	}
2678135446Strhodes
2679135446Strhodes	if (global_event == NULL)
2680135446Strhodes		global_event = event;
2681135446Strhodes
2682135446Strhodes	reset_system();
2683135446Strhodes	more = user_interaction();
2684135446Strhodes	if (!more) {
2685135446Strhodes		isc_app_shutdown();
2686135446Strhodes		return;
2687135446Strhodes	}
2688135446Strhodes	start_update();
2689135446Strhodes	return;
2690135446Strhodes}
2691135446Strhodes
2692135446Strhodesint
2693135446Strhodesmain(int argc, char **argv) {
2694135446Strhodes	isc_result_t result;
2695135446Strhodes	style = &dns_master_style_debug;
2696135446Strhodes
2697135446Strhodes	input = stdin;
2698135446Strhodes
2699135446Strhodes	interactive = ISC_TF(isatty(0));
2700135446Strhodes
2701135446Strhodes	isc_app_start();
2702135446Strhodes
2703193149Sdougb	pre_parse_args(argc, argv);
2704135446Strhodes
2705193149Sdougb	result = isc_mem_create(0, 0, &mctx);
2706193149Sdougb	check_result(result, "isc_mem_create");
2707193149Sdougb
2708193149Sdougb	parse_args(argc, argv, mctx, &entropy);
2709193149Sdougb
2710135446Strhodes	setup_system();
2711135446Strhodes
2712135446Strhodes	result = isc_app_onrun(mctx, global_task, getinput, NULL);
2713135446Strhodes	check_result(result, "isc_app_onrun");
2714135446Strhodes
2715135446Strhodes	(void)isc_app_run();
2716135446Strhodes
2717135446Strhodes	cleanup();
2718135446Strhodes
2719135446Strhodes	isc_app_finish();
2720135446Strhodes
2721135446Strhodes	if (seenerror)
2722135446Strhodes		return (2);
2723135446Strhodes	else
2724135446Strhodes		return (0);
2725135446Strhodes}
2726