nsupdate.c revision 222395
1135446Strhodes/*
2218384Sdougb * Copyright (C) 2004-2010  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
18218384Sdougb/* $Id: nsupdate.c,v 1.163.48.15 2010-12-09 04:30:57 tbox 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>
41218384Sdougb#include <isc/print.h>
42193149Sdougb#include <isc/random.h>
43135446Strhodes#include <isc/region.h>
44135446Strhodes#include <isc/sockaddr.h>
45135446Strhodes#include <isc/socket.h>
46135446Strhodes#include <isc/stdio.h>
47135446Strhodes#include <isc/string.h>
48135446Strhodes#include <isc/task.h>
49135446Strhodes#include <isc/timer.h>
50135446Strhodes#include <isc/types.h>
51135446Strhodes#include <isc/util.h>
52135446Strhodes
53135446Strhodes#include <dns/callbacks.h>
54135446Strhodes#include <dns/dispatch.h>
55135446Strhodes#include <dns/dnssec.h>
56135446Strhodes#include <dns/events.h>
57135446Strhodes#include <dns/fixedname.h>
58193149Sdougb#include <dns/log.h>
59135446Strhodes#include <dns/masterdump.h>
60135446Strhodes#include <dns/message.h>
61135446Strhodes#include <dns/name.h>
62135446Strhodes#include <dns/rcode.h>
63135446Strhodes#include <dns/rdata.h>
64135446Strhodes#include <dns/rdataclass.h>
65135446Strhodes#include <dns/rdatalist.h>
66135446Strhodes#include <dns/rdataset.h>
67135446Strhodes#include <dns/rdatastruct.h>
68135446Strhodes#include <dns/rdatatype.h>
69135446Strhodes#include <dns/request.h>
70135446Strhodes#include <dns/result.h>
71193149Sdougb#include <dns/tkey.h>
72135446Strhodes#include <dns/tsig.h>
73135446Strhodes
74135446Strhodes#include <dst/dst.h>
75135446Strhodes
76135446Strhodes#include <lwres/lwres.h>
77135446Strhodes#include <lwres/net.h>
78135446Strhodes
79193149Sdougb#ifdef GSSAPI
80193149Sdougb#include <dst/gssapi.h>
81193149Sdougb#endif
82135446Strhodes#include <bind9/getaddresses.h>
83135446Strhodes
84193149Sdougb
85135446Strhodes#ifdef HAVE_ADDRINFO
86135446Strhodes#ifdef HAVE_GETADDRINFO
87135446Strhodes#ifdef HAVE_GAISTRERROR
88135446Strhodes#define USE_GETADDRINFO
89135446Strhodes#endif
90135446Strhodes#endif
91135446Strhodes#endif
92135446Strhodes
93135446Strhodes#ifndef USE_GETADDRINFO
94135446Strhodes#ifndef ISC_PLATFORM_NONSTDHERRNO
95135446Strhodesextern int h_errno;
96135446Strhodes#endif
97135446Strhodes#endif
98135446Strhodes
99135446Strhodes#define MAXCMD (4 * 1024)
100135446Strhodes#define MAXWIRE (64 * 1024)
101135446Strhodes#define PACKETSIZE ((64 * 1024) - 1)
102135446Strhodes#define INITTEXT (2 * 1024)
103135446Strhodes#define MAXTEXT (128 * 1024)
104135446Strhodes#define FIND_TIMEOUT 5
105135446Strhodes#define TTL_MAX 2147483647U	/* Maximum signed 32 bit integer. */
106135446Strhodes
107135446Strhodes#define DNSDEFAULTPORT 53
108135446Strhodes
109135446Strhodes#ifndef RESOLV_CONF
110135446Strhodes#define RESOLV_CONF "/etc/resolv.conf"
111135446Strhodes#endif
112135446Strhodes
113135446Strhodesstatic isc_boolean_t debugging = ISC_FALSE, ddebugging = ISC_FALSE;
114135446Strhodesstatic isc_boolean_t memdebugging = ISC_FALSE;
115135446Strhodesstatic isc_boolean_t have_ipv4 = ISC_FALSE;
116135446Strhodesstatic isc_boolean_t have_ipv6 = ISC_FALSE;
117135446Strhodesstatic isc_boolean_t is_dst_up = ISC_FALSE;
118135446Strhodesstatic isc_boolean_t usevc = ISC_FALSE;
119193149Sdougbstatic isc_boolean_t usegsstsig = ISC_FALSE;
120193149Sdougbstatic isc_boolean_t use_win2k_gsstsig = ISC_FALSE;
121193149Sdougbstatic isc_boolean_t tried_other_gsstsig = ISC_FALSE;
122135446Strhodesstatic isc_taskmgr_t *taskmgr = NULL;
123135446Strhodesstatic isc_task_t *global_task = NULL;
124135446Strhodesstatic isc_event_t *global_event = NULL;
125193149Sdougbstatic isc_log_t *lctx = NULL;
126135446Strhodesstatic isc_mem_t *mctx = NULL;
127135446Strhodesstatic dns_dispatchmgr_t *dispatchmgr = NULL;
128135446Strhodesstatic dns_requestmgr_t *requestmgr = NULL;
129135446Strhodesstatic isc_socketmgr_t *socketmgr = NULL;
130135446Strhodesstatic isc_timermgr_t *timermgr = NULL;
131135446Strhodesstatic dns_dispatch_t *dispatchv4 = NULL;
132135446Strhodesstatic dns_dispatch_t *dispatchv6 = NULL;
133135446Strhodesstatic dns_message_t *updatemsg = NULL;
134135446Strhodesstatic dns_fixedname_t fuserzone;
135135446Strhodesstatic dns_name_t *userzone = NULL;
136193149Sdougbstatic dns_name_t *zonename = NULL;
137193149Sdougbstatic dns_name_t tmpzonename;
138193149Sdougbstatic dns_name_t restart_master;
139193149Sdougbstatic dns_tsig_keyring_t *gssring = NULL;
140135446Strhodesstatic dns_tsigkey_t *tsigkey = NULL;
141135446Strhodesstatic dst_key_t *sig0key;
142135446Strhodesstatic lwres_context_t *lwctx = NULL;
143135446Strhodesstatic lwres_conf_t *lwconf;
144135446Strhodesstatic isc_sockaddr_t *servers;
145135446Strhodesstatic int ns_inuse = 0;
146135446Strhodesstatic int ns_total = 0;
147135446Strhodesstatic isc_sockaddr_t *userserver = NULL;
148135446Strhodesstatic isc_sockaddr_t *localaddr = NULL;
149193149Sdougbstatic isc_sockaddr_t *serveraddr = NULL;
150193149Sdougbstatic isc_sockaddr_t tempaddr;
151135446Strhodesstatic char *keystr = NULL, *keyfile = NULL;
152193149Sdougbstatic isc_entropy_t *entropy = NULL;
153135446Strhodesstatic isc_boolean_t shuttingdown = ISC_FALSE;
154135446Strhodesstatic FILE *input;
155135446Strhodesstatic isc_boolean_t interactive = ISC_TRUE;
156135446Strhodesstatic isc_boolean_t seenerror = ISC_FALSE;
157135446Strhodesstatic const dns_master_style_t *style;
158135446Strhodesstatic int requests = 0;
159193149Sdougbstatic unsigned int logdebuglevel = 0;
160135446Strhodesstatic unsigned int timeout = 300;
161135446Strhodesstatic unsigned int udp_timeout = 3;
162135446Strhodesstatic unsigned int udp_retries = 3;
163135446Strhodesstatic dns_rdataclass_t defaultclass = dns_rdataclass_in;
164135446Strhodesstatic dns_rdataclass_t zoneclass = dns_rdataclass_none;
165135446Strhodesstatic dns_message_t *answer = NULL;
166193149Sdougbstatic isc_uint32_t default_ttl = 0;
167193149Sdougbstatic isc_boolean_t default_ttl_set = ISC_FALSE;
168135446Strhodes
169135446Strhodestypedef struct nsu_requestinfo {
170135446Strhodes	dns_message_t *msg;
171135446Strhodes	isc_sockaddr_t *addr;
172135446Strhodes} nsu_requestinfo_t;
173135446Strhodes
174135446Strhodesstatic void
175135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
176135446Strhodes	    dns_message_t *msg, dns_request_t **request);
177135446Strhodesstatic void
178135446Strhodesfatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
179135446Strhodes
180135446Strhodesstatic void
181135446Strhodesdebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
182135446Strhodes
183135446Strhodesstatic void
184135446Strhodesddebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
185135446Strhodes
186193149Sdougb#ifdef GSSAPI
187193149Sdougbstatic dns_fixedname_t fkname;
188193149Sdougbstatic isc_sockaddr_t *kserver = NULL;
189218384Sdougbstatic char *realm = NULL;
190193149Sdougbstatic char servicename[DNS_NAME_FORMATSIZE];
191193149Sdougbstatic dns_name_t *keyname;
192193149Sdougbtypedef struct nsu_gssinfo {
193193149Sdougb	dns_message_t *msg;
194193149Sdougb	isc_sockaddr_t *addr;
195193149Sdougb	gss_ctx_id_t context;
196193149Sdougb} nsu_gssinfo_t;
197193149Sdougb
198170222Sdougbstatic void
199193149Sdougbstart_gssrequest(dns_name_t *master);
200193149Sdougbstatic void
201193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
202193149Sdougb		dns_message_t *msg, dns_request_t **request,
203193149Sdougb		gss_ctx_id_t context);
204193149Sdougbstatic void
205193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event);
206193149Sdougb#endif /* GSSAPI */
207193149Sdougb
208193149Sdougbstatic void
209170222Sdougberror(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
210170222Sdougb
211135446Strhodes#define STATUS_MORE	(isc_uint16_t)0
212135446Strhodes#define STATUS_SEND	(isc_uint16_t)1
213135446Strhodes#define STATUS_QUIT	(isc_uint16_t)2
214135446Strhodes#define STATUS_SYNTAX	(isc_uint16_t)3
215135446Strhodes
216193149Sdougbtypedef struct entropysource entropysource_t;
217193149Sdougb
218193149Sdougbstruct entropysource {
219193149Sdougb	isc_entropysource_t *source;
220193149Sdougb	isc_mem_t *mctx;
221193149Sdougb	ISC_LINK(entropysource_t) link;
222193149Sdougb};
223193149Sdougb
224193149Sdougbstatic ISC_LIST(entropysource_t) sources;
225193149Sdougb
226193149Sdougbstatic void
227193149Sdougbsetup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx)
228193149Sdougb{
229193149Sdougb	isc_result_t result;
230193149Sdougb	isc_entropysource_t *source = NULL;
231193149Sdougb	entropysource_t *elt;
232193149Sdougb	int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
233193149Sdougb
234193149Sdougb	REQUIRE(ectx != NULL);
235193149Sdougb
236193149Sdougb	if (*ectx == NULL) {
237193149Sdougb		result = isc_entropy_create(mctx, ectx);
238193149Sdougb		if (result != ISC_R_SUCCESS)
239193149Sdougb			fatal("could not create entropy object");
240193149Sdougb		ISC_LIST_INIT(sources);
241193149Sdougb	}
242193149Sdougb
243193149Sdougb	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
244193149Sdougb		usekeyboard = ISC_ENTROPY_KEYBOARDYES;
245193149Sdougb		randomfile = NULL;
246193149Sdougb	}
247193149Sdougb
248193149Sdougb	result = isc_entropy_usebestsource(*ectx, &source, randomfile,
249193149Sdougb					   usekeyboard);
250193149Sdougb
251193149Sdougb	if (result != ISC_R_SUCCESS)
252193149Sdougb		fatal("could not initialize entropy source: %s",
253193149Sdougb		      isc_result_totext(result));
254193149Sdougb
255193149Sdougb	if (source != NULL) {
256193149Sdougb		elt = isc_mem_get(mctx, sizeof(*elt));
257193149Sdougb		if (elt == NULL)
258193149Sdougb			fatal("out of memory");
259193149Sdougb		elt->source = source;
260193149Sdougb		elt->mctx = mctx;
261193149Sdougb		ISC_LINK_INIT(elt, link);
262193149Sdougb		ISC_LIST_APPEND(sources, elt, link);
263193149Sdougb	}
264193149Sdougb}
265193149Sdougb
266193149Sdougbstatic void
267193149Sdougbcleanup_entropy(isc_entropy_t **ectx) {
268193149Sdougb	entropysource_t *source;
269193149Sdougb	while (!ISC_LIST_EMPTY(sources)) {
270193149Sdougb		source = ISC_LIST_HEAD(sources);
271193149Sdougb		ISC_LIST_UNLINK(sources, source, link);
272193149Sdougb		isc_entropy_destroysource(&source->source);
273193149Sdougb		isc_mem_put(source->mctx, source, sizeof(*source));
274193149Sdougb	}
275193149Sdougb	isc_entropy_detach(ectx);
276193149Sdougb}
277193149Sdougb
278193149Sdougb
279135446Strhodesstatic dns_rdataclass_t
280135446Strhodesgetzoneclass(void) {
281135446Strhodes	if (zoneclass == dns_rdataclass_none)
282135446Strhodes		zoneclass = defaultclass;
283135446Strhodes	return (zoneclass);
284135446Strhodes}
285135446Strhodes
286135446Strhodesstatic isc_boolean_t
287135446Strhodessetzoneclass(dns_rdataclass_t rdclass) {
288135446Strhodes	if (zoneclass == dns_rdataclass_none ||
289135446Strhodes	    rdclass == dns_rdataclass_none)
290135446Strhodes		zoneclass = rdclass;
291135446Strhodes	if (zoneclass != rdclass)
292135446Strhodes		return (ISC_FALSE);
293135446Strhodes	return (ISC_TRUE);
294135446Strhodes}
295135446Strhodes
296135446Strhodesstatic void
297135446Strhodesfatal(const char *format, ...) {
298135446Strhodes	va_list args;
299135446Strhodes
300135446Strhodes	va_start(args, format);
301135446Strhodes	vfprintf(stderr, format, args);
302135446Strhodes	va_end(args);
303135446Strhodes	fprintf(stderr, "\n");
304135446Strhodes	exit(1);
305135446Strhodes}
306135446Strhodes
307135446Strhodesstatic void
308170222Sdougberror(const char *format, ...) {
309170222Sdougb	va_list args;
310170222Sdougb
311170222Sdougb	va_start(args, format);
312170222Sdougb	vfprintf(stderr, format, args);
313170222Sdougb	va_end(args);
314170222Sdougb	fprintf(stderr, "\n");
315170222Sdougb}
316170222Sdougb
317170222Sdougbstatic void
318135446Strhodesdebug(const char *format, ...) {
319135446Strhodes	va_list args;
320135446Strhodes
321135446Strhodes	if (debugging) {
322135446Strhodes		va_start(args, format);
323135446Strhodes		vfprintf(stderr, format, args);
324135446Strhodes		va_end(args);
325135446Strhodes		fprintf(stderr, "\n");
326135446Strhodes	}
327135446Strhodes}
328135446Strhodes
329135446Strhodesstatic void
330135446Strhodesddebug(const char *format, ...) {
331135446Strhodes	va_list args;
332135446Strhodes
333135446Strhodes	if (ddebugging) {
334135446Strhodes		va_start(args, format);
335135446Strhodes		vfprintf(stderr, format, args);
336135446Strhodes		va_end(args);
337135446Strhodes		fprintf(stderr, "\n");
338135446Strhodes	}
339135446Strhodes}
340135446Strhodes
341135446Strhodesstatic inline void
342135446Strhodescheck_result(isc_result_t result, const char *msg) {
343135446Strhodes	if (result != ISC_R_SUCCESS)
344135446Strhodes		fatal("%s: %s", msg, isc_result_totext(result));
345135446Strhodes}
346135446Strhodes
347135446Strhodesstatic void *
348135446Strhodesmem_alloc(void *arg, size_t size) {
349135446Strhodes	return (isc_mem_get(arg, size));
350135446Strhodes}
351135446Strhodes
352135446Strhodesstatic void
353135446Strhodesmem_free(void *arg, void *mem, size_t size) {
354135446Strhodes	isc_mem_put(arg, mem, size);
355135446Strhodes}
356135446Strhodes
357135446Strhodesstatic char *
358135446Strhodesnsu_strsep(char **stringp, const char *delim) {
359135446Strhodes	char *string = *stringp;
360135446Strhodes	char *s;
361135446Strhodes	const char *d;
362135446Strhodes	char sc, dc;
363135446Strhodes
364135446Strhodes	if (string == NULL)
365135446Strhodes		return (NULL);
366135446Strhodes
367135446Strhodes	for (; *string != '\0'; string++) {
368135446Strhodes		sc = *string;
369135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
370135446Strhodes			if (sc == dc)
371135446Strhodes				break;
372135446Strhodes		}
373135446Strhodes		if (dc == 0)
374135446Strhodes			break;
375135446Strhodes	}
376135446Strhodes
377135446Strhodes	for (s = string; *s != '\0'; s++) {
378135446Strhodes		sc = *s;
379135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
380135446Strhodes			if (sc == dc) {
381135446Strhodes				*s++ = '\0';
382135446Strhodes				*stringp = s;
383135446Strhodes				return (string);
384135446Strhodes			}
385135446Strhodes		}
386135446Strhodes	}
387135446Strhodes	*stringp = NULL;
388135446Strhodes	return (string);
389135446Strhodes}
390135446Strhodes
391135446Strhodesstatic void
392135446Strhodesreset_system(void) {
393135446Strhodes	isc_result_t result;
394135446Strhodes
395135446Strhodes	ddebug("reset_system()");
396135446Strhodes	/* If the update message is still around, destroy it */
397135446Strhodes	if (updatemsg != NULL)
398135446Strhodes		dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER);
399135446Strhodes	else {
400135446Strhodes		result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
401135446Strhodes					    &updatemsg);
402135446Strhodes		check_result(result, "dns_message_create");
403135446Strhodes	}
404135446Strhodes	updatemsg->opcode = dns_opcode_update;
405193149Sdougb	if (usegsstsig) {
406193149Sdougb		if (tsigkey != NULL)
407193149Sdougb			dns_tsigkey_detach(&tsigkey);
408193149Sdougb		if (gssring != NULL)
409193149Sdougb			dns_tsigkeyring_destroy(&gssring);
410193149Sdougb		tried_other_gsstsig = ISC_FALSE;
411193149Sdougb	}
412135446Strhodes}
413135446Strhodes
414170222Sdougbstatic isc_uint16_t
415170222Sdougbparse_hmac(dns_name_t **hmac, const char *hmacstr, size_t len) {
416170222Sdougb	isc_uint16_t digestbits = 0;
417170222Sdougb	isc_result_t result;
418170222Sdougb	char buf[20];
419170222Sdougb
420170222Sdougb	REQUIRE(hmac != NULL && *hmac == NULL);
421170222Sdougb	REQUIRE(hmacstr != NULL);
422170222Sdougb
423170222Sdougb	if (len >= sizeof(buf))
424170222Sdougb		fatal("unknown key type '%.*s'", (int)(len), hmacstr);
425170222Sdougb
426170222Sdougb	strncpy(buf, hmacstr, len);
427170222Sdougb	buf[len] = 0;
428186462Sdougb
429170222Sdougb	if (strcasecmp(buf, "hmac-md5") == 0) {
430170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
431170222Sdougb	} else if (strncasecmp(buf, "hmac-md5-", 9) == 0) {
432170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
433170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[9], 10);
434170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 128)
435170222Sdougb			fatal("digest-bits out of range [0..128]");
436170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
437170222Sdougb	} else if (strcasecmp(buf, "hmac-sha1") == 0) {
438170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
439170222Sdougb	} else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) {
440170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
441170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[10], 10);
442170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 160)
443170222Sdougb			fatal("digest-bits out of range [0..160]");
444170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
445170222Sdougb	} else if (strcasecmp(buf, "hmac-sha224") == 0) {
446170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
447170222Sdougb	} else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) {
448170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
449170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
450170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 224)
451170222Sdougb			fatal("digest-bits out of range [0..224]");
452170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
453170222Sdougb	} else if (strcasecmp(buf, "hmac-sha256") == 0) {
454170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
455170222Sdougb	} else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) {
456170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
457170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
458170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 256)
459170222Sdougb			fatal("digest-bits out of range [0..256]");
460170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
461170222Sdougb	} else if (strcasecmp(buf, "hmac-sha384") == 0) {
462170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
463170222Sdougb	} else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) {
464170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
465170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
466170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 384)
467170222Sdougb			fatal("digest-bits out of range [0..384]");
468170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
469170222Sdougb	} else if (strcasecmp(buf, "hmac-sha512") == 0) {
470170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
471170222Sdougb	} else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) {
472170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
473170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
474170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 512)
475170222Sdougb			fatal("digest-bits out of range [0..512]");
476170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
477170222Sdougb	} else
478170222Sdougb		fatal("unknown key type '%s'", buf);
479170222Sdougb	return (digestbits);
480170222Sdougb}
481170222Sdougb
482135446Strhodesstatic void
483135446Strhodessetup_keystr(void) {
484135446Strhodes	unsigned char *secret = NULL;
485135446Strhodes	int secretlen;
486135446Strhodes	isc_buffer_t secretbuf;
487135446Strhodes	isc_result_t result;
488135446Strhodes	isc_buffer_t keynamesrc;
489135446Strhodes	char *secretstr;
490170222Sdougb	char *s, *n;
491135446Strhodes	dns_fixedname_t fkeyname;
492135446Strhodes	dns_name_t *keyname;
493170222Sdougb	char *name;
494170222Sdougb	dns_name_t *hmacname = NULL;
495170222Sdougb	isc_uint16_t digestbits = 0;
496135446Strhodes
497135446Strhodes	dns_fixedname_init(&fkeyname);
498135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
499135446Strhodes
500135446Strhodes	debug("Creating key...");
501135446Strhodes
502135446Strhodes	s = strchr(keystr, ':');
503170222Sdougb	if (s == NULL || s == keystr || s[1] == 0)
504170222Sdougb		fatal("key option must specify [hmac:]keyname:secret");
505135446Strhodes	secretstr = s + 1;
506170222Sdougb	n = strchr(secretstr, ':');
507170222Sdougb	if (n != NULL) {
508170222Sdougb		if (n == secretstr || n[1] == 0)
509170222Sdougb			fatal("key option must specify [hmac:]keyname:secret");
510170222Sdougb		name = secretstr;
511170222Sdougb		secretstr = n + 1;
512170222Sdougb		digestbits = parse_hmac(&hmacname, keystr, s - keystr);
513170222Sdougb	} else {
514170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
515170222Sdougb		name = keystr;
516170222Sdougb		n = s;
517170222Sdougb	}
518135446Strhodes
519170222Sdougb	isc_buffer_init(&keynamesrc, name, n - name);
520170222Sdougb	isc_buffer_add(&keynamesrc, n - name);
521135446Strhodes
522135446Strhodes	debug("namefromtext");
523135446Strhodes	result = dns_name_fromtext(keyname, &keynamesrc, dns_rootname,
524135446Strhodes				   ISC_FALSE, NULL);
525135446Strhodes	check_result(result, "dns_name_fromtext");
526135446Strhodes
527135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
528135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
529135446Strhodes	if (secret == NULL)
530135446Strhodes		fatal("out of memory");
531135446Strhodes
532135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
533135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
534135446Strhodes	if (result != ISC_R_SUCCESS) {
535135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
536135446Strhodes			keystr, isc_result_totext(result));
537135446Strhodes		goto failure;
538135446Strhodes	}
539135446Strhodes
540135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
541135446Strhodes
542135446Strhodes	debug("keycreate");
543170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
544218384Sdougb				    ISC_FALSE, NULL, 0, 0, mctx, NULL,
545218384Sdougb				    &tsigkey);
546135446Strhodes	if (result != ISC_R_SUCCESS)
547135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
548135446Strhodes			keystr, dns_result_totext(result));
549170222Sdougb	else
550170222Sdougb		dst_key_setbits(tsigkey->key, digestbits);
551135446Strhodes failure:
552135446Strhodes	if (secret != NULL)
553135446Strhodes		isc_mem_free(mctx, secret);
554135446Strhodes}
555135446Strhodes
556218384Sdougbstatic int
557218384Sdougbbasenamelen(const char *file) {
558218384Sdougb	int len = strlen(file);
559218384Sdougb
560218384Sdougb	if (len > 1 && file[len - 1] == '.')
561218384Sdougb		len -= 1;
562218384Sdougb	else if (len > 8 && strcmp(file + len - 8, ".private") == 0)
563218384Sdougb		len -= 8;
564218384Sdougb	else if (len > 4 && strcmp(file + len - 4, ".key") == 0)
565218384Sdougb		len -= 4;
566218384Sdougb	return (len);
567218384Sdougb}
568218384Sdougb
569135446Strhodesstatic void
570135446Strhodessetup_keyfile(void) {
571135446Strhodes	dst_key_t *dstkey = NULL;
572135446Strhodes	isc_result_t result;
573170222Sdougb	dns_name_t *hmacname = NULL;
574135446Strhodes
575135446Strhodes	debug("Creating key...");
576135446Strhodes
577218384Sdougb	if (sig0key != NULL)
578218384Sdougb		dst_key_free(&sig0key);
579218384Sdougb
580135446Strhodes	result = dst_key_fromnamedfile(keyfile,
581135446Strhodes				       DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
582135446Strhodes				       &dstkey);
583135446Strhodes	if (result != ISC_R_SUCCESS) {
584218384Sdougb		fprintf(stderr, "could not read key from %.*s.{private,key}: "
585218384Sdougb				"%s\n", basenamelen(keyfile), keyfile,
586218384Sdougb				isc_result_totext(result));
587135446Strhodes		return;
588135446Strhodes	}
589170222Sdougb	switch (dst_key_alg(dstkey)) {
590170222Sdougb	case DST_ALG_HMACMD5:
591170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
592170222Sdougb		break;
593170222Sdougb	case DST_ALG_HMACSHA1:
594170222Sdougb		hmacname = DNS_TSIG_HMACSHA1_NAME;
595170222Sdougb		break;
596170222Sdougb	case DST_ALG_HMACSHA224:
597170222Sdougb		hmacname = DNS_TSIG_HMACSHA224_NAME;
598170222Sdougb		break;
599170222Sdougb	case DST_ALG_HMACSHA256:
600170222Sdougb		hmacname = DNS_TSIG_HMACSHA256_NAME;
601170222Sdougb		break;
602170222Sdougb	case DST_ALG_HMACSHA384:
603170222Sdougb		hmacname = DNS_TSIG_HMACSHA384_NAME;
604170222Sdougb		break;
605170222Sdougb	case DST_ALG_HMACSHA512:
606170222Sdougb		hmacname = DNS_TSIG_HMACSHA512_NAME;
607170222Sdougb		break;
608170222Sdougb	}
609170222Sdougb	if (hmacname != NULL) {
610135446Strhodes		result = dns_tsigkey_createfromkey(dst_key_name(dstkey),
611170222Sdougb						   hmacname, dstkey, ISC_FALSE,
612170222Sdougb						   NULL, 0, 0, mctx, NULL,
613170222Sdougb						   &tsigkey);
614218384Sdougb		dst_key_free(&dstkey);
615135446Strhodes		if (result != ISC_R_SUCCESS) {
616135446Strhodes			fprintf(stderr, "could not create key from %s: %s\n",
617135446Strhodes				keyfile, isc_result_totext(result));
618135446Strhodes			return;
619135446Strhodes		}
620222395Sdougb	} else {
621218384Sdougb		dst_key_attach(dstkey, &sig0key);
622222395Sdougb		dst_key_free(&dstkey);
623222395Sdougb	}
624135446Strhodes}
625135446Strhodes
626135446Strhodesstatic void
627135446Strhodesdoshutdown(void) {
628135446Strhodes	isc_task_detach(&global_task);
629135446Strhodes
630135446Strhodes	if (userserver != NULL)
631135446Strhodes		isc_mem_put(mctx, userserver, sizeof(isc_sockaddr_t));
632135446Strhodes
633135446Strhodes	if (localaddr != NULL)
634135446Strhodes		isc_mem_put(mctx, localaddr, sizeof(isc_sockaddr_t));
635135446Strhodes
636135446Strhodes	if (tsigkey != NULL) {
637135446Strhodes		ddebug("Freeing TSIG key");
638135446Strhodes		dns_tsigkey_detach(&tsigkey);
639135446Strhodes	}
640135446Strhodes
641135446Strhodes	if (sig0key != NULL) {
642135446Strhodes		ddebug("Freeing SIG(0) key");
643135446Strhodes		dst_key_free(&sig0key);
644135446Strhodes	}
645135446Strhodes
646135446Strhodes	if (updatemsg != NULL)
647135446Strhodes		dns_message_destroy(&updatemsg);
648135446Strhodes
649135446Strhodes	if (is_dst_up) {
650135446Strhodes		ddebug("Destroy DST lib");
651135446Strhodes		dst_lib_destroy();
652135446Strhodes		is_dst_up = ISC_FALSE;
653135446Strhodes	}
654135446Strhodes
655193149Sdougb	cleanup_entropy(&entropy);
656135446Strhodes
657135446Strhodes	lwres_conf_clear(lwctx);
658135446Strhodes	lwres_context_destroy(&lwctx);
659135446Strhodes
660135446Strhodes	isc_mem_put(mctx, servers, ns_total * sizeof(isc_sockaddr_t));
661135446Strhodes
662135446Strhodes	ddebug("Destroying request manager");
663135446Strhodes	dns_requestmgr_detach(&requestmgr);
664135446Strhodes
665135446Strhodes	ddebug("Freeing the dispatchers");
666135446Strhodes	if (have_ipv4)
667135446Strhodes		dns_dispatch_detach(&dispatchv4);
668135446Strhodes	if (have_ipv6)
669135446Strhodes		dns_dispatch_detach(&dispatchv6);
670135446Strhodes
671135446Strhodes	ddebug("Shutting down dispatch manager");
672135446Strhodes	dns_dispatchmgr_destroy(&dispatchmgr);
673135446Strhodes
674135446Strhodes}
675135446Strhodes
676135446Strhodesstatic void
677135446Strhodesmaybeshutdown(void) {
678135446Strhodes	ddebug("Shutting down request manager");
679135446Strhodes	dns_requestmgr_shutdown(requestmgr);
680135446Strhodes
681135446Strhodes	if (requests != 0)
682135446Strhodes		return;
683135446Strhodes
684135446Strhodes	doshutdown();
685135446Strhodes}
686135446Strhodes
687135446Strhodesstatic void
688135446Strhodesshutdown_program(isc_task_t *task, isc_event_t *event) {
689135446Strhodes	REQUIRE(task == global_task);
690135446Strhodes	UNUSED(task);
691135446Strhodes
692135446Strhodes	ddebug("shutdown_program()");
693135446Strhodes	isc_event_free(&event);
694135446Strhodes
695135446Strhodes	shuttingdown = ISC_TRUE;
696135446Strhodes	maybeshutdown();
697135446Strhodes}
698135446Strhodes
699135446Strhodesstatic void
700135446Strhodessetup_system(void) {
701135446Strhodes	isc_result_t result;
702135446Strhodes	isc_sockaddr_t bind_any, bind_any6;
703135446Strhodes	lwres_result_t lwresult;
704135446Strhodes	unsigned int attrs, attrmask;
705135446Strhodes	int i;
706193149Sdougb	isc_logconfig_t *logconfig = NULL;
707135446Strhodes
708135446Strhodes	ddebug("setup_system()");
709135446Strhodes
710135446Strhodes	dns_result_register();
711135446Strhodes
712135446Strhodes	result = isc_net_probeipv4();
713135446Strhodes	if (result == ISC_R_SUCCESS)
714135446Strhodes		have_ipv4 = ISC_TRUE;
715135446Strhodes
716135446Strhodes	result = isc_net_probeipv6();
717135446Strhodes	if (result == ISC_R_SUCCESS)
718135446Strhodes		have_ipv6 = ISC_TRUE;
719135446Strhodes
720135446Strhodes	if (!have_ipv4 && !have_ipv6)
721135446Strhodes		fatal("could not find either IPv4 or IPv6");
722135446Strhodes
723193149Sdougb	result = isc_log_create(mctx, &lctx, &logconfig);
724193149Sdougb	check_result(result, "isc_log_create");
725135446Strhodes
726193149Sdougb	isc_log_setcontext(lctx);
727193149Sdougb	dns_log_init(lctx);
728193149Sdougb	dns_log_setcontext(lctx);
729193149Sdougb
730193149Sdougb	result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
731193149Sdougb	check_result(result, "isc_log_usechannel");
732193149Sdougb
733193149Sdougb	isc_log_setdebuglevel(lctx, logdebuglevel);
734193149Sdougb
735135446Strhodes	lwresult = lwres_context_create(&lwctx, mctx, mem_alloc, mem_free, 1);
736135446Strhodes	if (lwresult != LWRES_R_SUCCESS)
737135446Strhodes		fatal("lwres_context_create failed");
738135446Strhodes
739135446Strhodes	(void)lwres_conf_parse(lwctx, RESOLV_CONF);
740135446Strhodes	lwconf = lwres_conf_get(lwctx);
741135446Strhodes
742135446Strhodes	ns_total = lwconf->nsnext;
743135446Strhodes	if (ns_total <= 0) {
744135446Strhodes		/* No name servers in resolv.conf; default to loopback. */
745135446Strhodes		struct in_addr localhost;
746135446Strhodes		ns_total = 1;
747135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
748135446Strhodes		if (servers == NULL)
749135446Strhodes			fatal("out of memory");
750135446Strhodes		localhost.s_addr = htonl(INADDR_LOOPBACK);
751135446Strhodes		isc_sockaddr_fromin(&servers[0], &localhost, DNSDEFAULTPORT);
752135446Strhodes	} else {
753135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
754135446Strhodes		if (servers == NULL)
755135446Strhodes			fatal("out of memory");
756135446Strhodes		for (i = 0; i < ns_total; i++) {
757135446Strhodes			if (lwconf->nameservers[i].family == LWRES_ADDRTYPE_V4) {
758135446Strhodes				struct in_addr in4;
759135446Strhodes				memcpy(&in4, lwconf->nameservers[i].address, 4);
760135446Strhodes				isc_sockaddr_fromin(&servers[i], &in4, DNSDEFAULTPORT);
761135446Strhodes			} else {
762135446Strhodes				struct in6_addr in6;
763135446Strhodes				memcpy(&in6, lwconf->nameservers[i].address, 16);
764135446Strhodes				isc_sockaddr_fromin6(&servers[i], &in6,
765135446Strhodes						     DNSDEFAULTPORT);
766135446Strhodes			}
767135446Strhodes		}
768135446Strhodes	}
769135446Strhodes
770193149Sdougb	setup_entropy(mctx, NULL, &entropy);
771135446Strhodes
772193149Sdougb	result = isc_hash_create(mctx, entropy, DNS_NAME_MAXWIRE);
773135446Strhodes	check_result(result, "isc_hash_create");
774135446Strhodes	isc_hash_init();
775135446Strhodes
776193149Sdougb	result = dns_dispatchmgr_create(mctx, entropy, &dispatchmgr);
777135446Strhodes	check_result(result, "dns_dispatchmgr_create");
778135446Strhodes
779135446Strhodes	result = isc_socketmgr_create(mctx, &socketmgr);
780135446Strhodes	check_result(result, "dns_socketmgr_create");
781135446Strhodes
782135446Strhodes	result = isc_timermgr_create(mctx, &timermgr);
783135446Strhodes	check_result(result, "dns_timermgr_create");
784135446Strhodes
785135446Strhodes	result = isc_taskmgr_create(mctx, 1, 0, &taskmgr);
786135446Strhodes	check_result(result, "isc_taskmgr_create");
787135446Strhodes
788135446Strhodes	result = isc_task_create(taskmgr, 0, &global_task);
789135446Strhodes	check_result(result, "isc_task_create");
790135446Strhodes
791135446Strhodes	result = isc_task_onshutdown(global_task, shutdown_program, NULL);
792135446Strhodes	check_result(result, "isc_task_onshutdown");
793135446Strhodes
794193149Sdougb	result = dst_lib_init(mctx, entropy, 0);
795135446Strhodes	check_result(result, "dst_lib_init");
796135446Strhodes	is_dst_up = ISC_TRUE;
797135446Strhodes
798135446Strhodes	attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
799135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
800135446Strhodes
801135446Strhodes	if (have_ipv6) {
802135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
803135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
804135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
805135446Strhodes		isc_sockaddr_any6(&bind_any6);
806135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
807135446Strhodes					     &bind_any6, PACKETSIZE,
808135446Strhodes					     4, 2, 3, 5,
809135446Strhodes					     attrs, attrmask, &dispatchv6);
810135446Strhodes		check_result(result, "dns_dispatch_getudp (v6)");
811135446Strhodes	}
812135446Strhodes
813135446Strhodes	if (have_ipv4) {
814135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
815135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
816135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
817135446Strhodes		isc_sockaddr_any(&bind_any);
818135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
819135446Strhodes					     &bind_any, PACKETSIZE,
820135446Strhodes					     4, 2, 3, 5,
821135446Strhodes					     attrs, attrmask, &dispatchv4);
822135446Strhodes		check_result(result, "dns_dispatch_getudp (v4)");
823135446Strhodes	}
824135446Strhodes
825135446Strhodes	result = dns_requestmgr_create(mctx, timermgr,
826135446Strhodes				       socketmgr, taskmgr, dispatchmgr,
827135446Strhodes				       dispatchv4, dispatchv6, &requestmgr);
828135446Strhodes	check_result(result, "dns_requestmgr_create");
829135446Strhodes
830135446Strhodes	if (keystr != NULL)
831135446Strhodes		setup_keystr();
832135446Strhodes	else if (keyfile != NULL)
833135446Strhodes		setup_keyfile();
834135446Strhodes}
835135446Strhodes
836135446Strhodesstatic void
837135446Strhodesget_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
838135446Strhodes	int count;
839135446Strhodes	isc_result_t result;
840135446Strhodes
841135446Strhodes	isc_app_block();
842135446Strhodes	result = bind9_getaddresses(host, port, sockaddr, 1, &count);
843135446Strhodes	isc_app_unblock();
844135446Strhodes	if (result != ISC_R_SUCCESS)
845135446Strhodes		fatal("couldn't get address for '%s': %s",
846135446Strhodes		      host, isc_result_totext(result));
847135446Strhodes	INSIST(count == 1);
848135446Strhodes}
849135446Strhodes
850193149Sdougb#define PARSE_ARGS_FMT "dDMl:y:govk:rR::t:u:"
851193149Sdougb
852135446Strhodesstatic void
853193149Sdougbpre_parse_args(int argc, char **argv) {
854135446Strhodes	int ch;
855193149Sdougb
856193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
857193149Sdougb		switch (ch) {
858193149Sdougb		case 'M': /* was -dm */
859193149Sdougb			debugging = ISC_TRUE;
860193149Sdougb			ddebugging = ISC_TRUE;
861193149Sdougb			memdebugging = ISC_TRUE;
862193149Sdougb			isc_mem_debugging = ISC_MEM_DEBUGTRACE |
863193149Sdougb					    ISC_MEM_DEBUGRECORD;
864193149Sdougb			break;
865193149Sdougb
866193149Sdougb		case '?':
867193149Sdougb			if (isc_commandline_option != '?')
868193149Sdougb				fprintf(stderr, "%s: invalid argument -%c\n",
869193149Sdougb					argv[0], isc_commandline_option);
870193149Sdougb			fprintf(stderr, "usage: nsupdate [-d] "
871193149Sdougb				"[-g | -o | -y keyname:secret | -k keyfile] "
872193149Sdougb				"[-v] [filename]\n");
873193149Sdougb			exit(1);
874193149Sdougb
875193149Sdougb		default:
876193149Sdougb			break;
877193149Sdougb		}
878193149Sdougb	}
879193149Sdougb	isc_commandline_reset = ISC_TRUE;
880193149Sdougb	isc_commandline_index = 1;
881193149Sdougb}
882193149Sdougb
883193149Sdougbstatic void
884193149Sdougbparse_args(int argc, char **argv, isc_mem_t *mctx, isc_entropy_t **ectx) {
885193149Sdougb	int ch;
886193149Sdougb	isc_uint32_t i;
887135446Strhodes	isc_result_t result;
888135446Strhodes
889135446Strhodes	debug("parse_args");
890193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
891135446Strhodes		switch (ch) {
892135446Strhodes		case 'd':
893135446Strhodes			debugging = ISC_TRUE;
894135446Strhodes			break;
895135446Strhodes		case 'D': /* was -dd */
896135446Strhodes			debugging = ISC_TRUE;
897135446Strhodes			ddebugging = ISC_TRUE;
898135446Strhodes			break;
899193149Sdougb		case 'M':
900135446Strhodes			break;
901193149Sdougb		case 'l':
902193149Sdougb			result = isc_parse_uint32(&i, isc_commandline_argument,
903193149Sdougb						  10);
904193149Sdougb			if (result != ISC_R_SUCCESS) {
905193149Sdougb				fprintf(stderr, "bad library debug value "
906193149Sdougb					"'%s'\n", isc_commandline_argument);
907193149Sdougb				exit(1);
908193149Sdougb			}
909193149Sdougb			logdebuglevel = i;
910193149Sdougb			break;
911135446Strhodes		case 'y':
912135446Strhodes			keystr = isc_commandline_argument;
913135446Strhodes			break;
914135446Strhodes		case 'v':
915135446Strhodes			usevc = ISC_TRUE;
916135446Strhodes			break;
917135446Strhodes		case 'k':
918135446Strhodes			keyfile = isc_commandline_argument;
919135446Strhodes			break;
920193149Sdougb		case 'g':
921193149Sdougb			usegsstsig = ISC_TRUE;
922193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
923193149Sdougb			break;
924193149Sdougb		case 'o':
925193149Sdougb			usegsstsig = ISC_TRUE;
926193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
927193149Sdougb			break;
928135446Strhodes		case 't':
929135446Strhodes			result = isc_parse_uint32(&timeout,
930135446Strhodes						  isc_commandline_argument, 10);
931135446Strhodes			if (result != ISC_R_SUCCESS) {
932135446Strhodes				fprintf(stderr, "bad timeout '%s'\n",						isc_commandline_argument);
933135446Strhodes				exit(1);
934135446Strhodes			}
935135446Strhodes			if (timeout == 0)
936143731Sdougb				timeout = UINT_MAX;
937135446Strhodes			break;
938135446Strhodes		case 'u':
939135446Strhodes			result = isc_parse_uint32(&udp_timeout,
940135446Strhodes						  isc_commandline_argument, 10);
941135446Strhodes			if (result != ISC_R_SUCCESS) {
942135446Strhodes				fprintf(stderr, "bad udp timeout '%s'\n",						isc_commandline_argument);
943135446Strhodes				exit(1);
944135446Strhodes			}
945135446Strhodes			if (udp_timeout == 0)
946143731Sdougb				udp_timeout = UINT_MAX;
947135446Strhodes			break;
948135446Strhodes		case 'r':
949135446Strhodes			result = isc_parse_uint32(&udp_retries,
950135446Strhodes						  isc_commandline_argument, 10);
951135446Strhodes			if (result != ISC_R_SUCCESS) {
952135446Strhodes				fprintf(stderr, "bad udp retries '%s'\n",						isc_commandline_argument);
953135446Strhodes				exit(1);
954135446Strhodes			}
955135446Strhodes			break;
956193149Sdougb
957193149Sdougb		case 'R':
958193149Sdougb			setup_entropy(mctx, isc_commandline_argument, ectx);
959193149Sdougb			break;
960193149Sdougb
961135446Strhodes		default:
962193149Sdougb			fprintf(stderr, "%s: unhandled option: %c\n",
963193149Sdougb				argv[0], isc_commandline_option);
964135446Strhodes			exit(1);
965135446Strhodes		}
966135446Strhodes	}
967135446Strhodes	if (keyfile != NULL && keystr != NULL) {
968135446Strhodes		fprintf(stderr, "%s: cannot specify both -k and -y\n",
969135446Strhodes			argv[0]);
970135446Strhodes		exit(1);
971135446Strhodes	}
972135446Strhodes
973193149Sdougb#ifdef GSSAPI
974193149Sdougb	if (usegsstsig && (keyfile != NULL || keystr != NULL)) {
975193149Sdougb		fprintf(stderr, "%s: cannot specify -g with -k or -y\n",
976193149Sdougb			argv[0]);
977193149Sdougb		exit(1);
978193149Sdougb	}
979193149Sdougb#else
980193149Sdougb	if (usegsstsig) {
981193149Sdougb		fprintf(stderr, "%s: cannot specify -g  or -o, " \
982193149Sdougb			"program not linked with GSS API Library\n",
983193149Sdougb			argv[0]);
984193149Sdougb		exit(1);
985193149Sdougb	}
986193149Sdougb#endif
987193149Sdougb
988135446Strhodes	if (argv[isc_commandline_index] != NULL) {
989135446Strhodes		if (strcmp(argv[isc_commandline_index], "-") == 0) {
990135446Strhodes			input = stdin;
991135446Strhodes		} else {
992135446Strhodes			result = isc_stdio_open(argv[isc_commandline_index],
993135446Strhodes						"r", &input);
994135446Strhodes			if (result != ISC_R_SUCCESS) {
995135446Strhodes				fprintf(stderr, "could not open '%s': %s\n",
996135446Strhodes					argv[isc_commandline_index],
997135446Strhodes					isc_result_totext(result));
998135446Strhodes				exit(1);
999135446Strhodes			}
1000135446Strhodes		}
1001135446Strhodes		interactive = ISC_FALSE;
1002135446Strhodes	}
1003135446Strhodes}
1004135446Strhodes
1005135446Strhodesstatic isc_uint16_t
1006135446Strhodesparse_name(char **cmdlinep, dns_message_t *msg, dns_name_t **namep) {
1007135446Strhodes	isc_result_t result;
1008135446Strhodes	char *word;
1009135446Strhodes	isc_buffer_t *namebuf = NULL;
1010135446Strhodes	isc_buffer_t source;
1011135446Strhodes
1012135446Strhodes	word = nsu_strsep(cmdlinep, " \t\r\n");
1013135446Strhodes	if (*word == 0) {
1014135446Strhodes		fprintf(stderr, "could not read owner name\n");
1015135446Strhodes		return (STATUS_SYNTAX);
1016135446Strhodes	}
1017135446Strhodes
1018135446Strhodes	result = dns_message_gettempname(msg, namep);
1019135446Strhodes	check_result(result, "dns_message_gettempname");
1020135446Strhodes	result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
1021135446Strhodes	check_result(result, "isc_buffer_allocate");
1022135446Strhodes	dns_name_init(*namep, NULL);
1023135446Strhodes	dns_name_setbuffer(*namep, namebuf);
1024135446Strhodes	dns_message_takebuffer(msg, &namebuf);
1025135446Strhodes	isc_buffer_init(&source, word, strlen(word));
1026135446Strhodes	isc_buffer_add(&source, strlen(word));
1027135446Strhodes	result = dns_name_fromtext(*namep, &source, dns_rootname,
1028135446Strhodes				   ISC_FALSE, NULL);
1029135446Strhodes	check_result(result, "dns_name_fromtext");
1030135446Strhodes	isc_buffer_invalidate(&source);
1031135446Strhodes	return (STATUS_MORE);
1032135446Strhodes}
1033135446Strhodes
1034135446Strhodesstatic isc_uint16_t
1035135446Strhodesparse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass,
1036135446Strhodes	    dns_rdatatype_t rdatatype, dns_message_t *msg,
1037135446Strhodes	    dns_rdata_t *rdata)
1038135446Strhodes{
1039135446Strhodes	char *cmdline = *cmdlinep;
1040135446Strhodes	isc_buffer_t source, *buf = NULL, *newbuf = NULL;
1041135446Strhodes	isc_region_t r;
1042135446Strhodes	isc_lex_t *lex = NULL;
1043135446Strhodes	dns_rdatacallbacks_t callbacks;
1044135446Strhodes	isc_result_t result;
1045135446Strhodes
1046135446Strhodes	while (*cmdline != 0 && isspace((unsigned char)*cmdline))
1047135446Strhodes		cmdline++;
1048135446Strhodes
1049135446Strhodes	if (*cmdline != 0) {
1050135446Strhodes		dns_rdatacallbacks_init(&callbacks);
1051135446Strhodes		result = isc_lex_create(mctx, strlen(cmdline), &lex);
1052135446Strhodes		check_result(result, "isc_lex_create");
1053135446Strhodes		isc_buffer_init(&source, cmdline, strlen(cmdline));
1054135446Strhodes		isc_buffer_add(&source, strlen(cmdline));
1055135446Strhodes		result = isc_lex_openbuffer(lex, &source);
1056135446Strhodes		check_result(result, "isc_lex_openbuffer");
1057135446Strhodes		result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
1058135446Strhodes		check_result(result, "isc_buffer_allocate");
1059193149Sdougb		result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex,
1060135446Strhodes					    dns_rootname, 0, mctx, buf,
1061135446Strhodes					    &callbacks);
1062135446Strhodes		isc_lex_destroy(&lex);
1063135446Strhodes		if (result == ISC_R_SUCCESS) {
1064135446Strhodes			isc_buffer_usedregion(buf, &r);
1065135446Strhodes			result = isc_buffer_allocate(mctx, &newbuf, r.length);
1066135446Strhodes			check_result(result, "isc_buffer_allocate");
1067135446Strhodes			isc_buffer_putmem(newbuf, r.base, r.length);
1068135446Strhodes			isc_buffer_usedregion(newbuf, &r);
1069135446Strhodes			dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
1070135446Strhodes			isc_buffer_free(&buf);
1071135446Strhodes			dns_message_takebuffer(msg, &newbuf);
1072135446Strhodes		} else {
1073135446Strhodes			fprintf(stderr, "invalid rdata format: %s\n",
1074135446Strhodes				isc_result_totext(result));
1075135446Strhodes			isc_buffer_free(&buf);
1076135446Strhodes			return (STATUS_SYNTAX);
1077135446Strhodes		}
1078135446Strhodes	} else {
1079135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1080135446Strhodes	}
1081135446Strhodes	*cmdlinep = cmdline;
1082135446Strhodes	return (STATUS_MORE);
1083135446Strhodes}
1084135446Strhodes
1085135446Strhodesstatic isc_uint16_t
1086135446Strhodesmake_prereq(char *cmdline, isc_boolean_t ispositive, isc_boolean_t isrrset) {
1087135446Strhodes	isc_result_t result;
1088135446Strhodes	char *word;
1089135446Strhodes	dns_name_t *name = NULL;
1090135446Strhodes	isc_textregion_t region;
1091135446Strhodes	dns_rdataset_t *rdataset = NULL;
1092135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1093135446Strhodes	dns_rdataclass_t rdataclass;
1094135446Strhodes	dns_rdatatype_t rdatatype;
1095135446Strhodes	dns_rdata_t *rdata = NULL;
1096135446Strhodes	isc_uint16_t retval;
1097135446Strhodes
1098135446Strhodes	ddebug("make_prereq()");
1099135446Strhodes
1100135446Strhodes	/*
1101135446Strhodes	 * Read the owner name
1102135446Strhodes	 */
1103135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1104135446Strhodes	if (retval != STATUS_MORE)
1105135446Strhodes		return (retval);
1106135446Strhodes
1107135446Strhodes	/*
1108135446Strhodes	 * If this is an rrset prereq, read the class or type.
1109135446Strhodes	 */
1110135446Strhodes	if (isrrset) {
1111135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1112135446Strhodes		if (*word == 0) {
1113135446Strhodes			fprintf(stderr, "could not read class or type\n");
1114135446Strhodes			goto failure;
1115135446Strhodes		}
1116135446Strhodes		region.base = word;
1117135446Strhodes		region.length = strlen(word);
1118135446Strhodes		result = dns_rdataclass_fromtext(&rdataclass, &region);
1119135446Strhodes		if (result == ISC_R_SUCCESS) {
1120135446Strhodes			if (!setzoneclass(rdataclass)) {
1121135446Strhodes				fprintf(stderr, "class mismatch: %s\n", word);
1122135446Strhodes				goto failure;
1123135446Strhodes			}
1124135446Strhodes			/*
1125135446Strhodes			 * Now read the type.
1126135446Strhodes			 */
1127135446Strhodes			word = nsu_strsep(&cmdline, " \t\r\n");
1128135446Strhodes			if (*word == 0) {
1129135446Strhodes				fprintf(stderr, "could not read type\n");
1130135446Strhodes				goto failure;
1131135446Strhodes			}
1132135446Strhodes			region.base = word;
1133135446Strhodes			region.length = strlen(word);
1134135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1135135446Strhodes			if (result != ISC_R_SUCCESS) {
1136135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1137135446Strhodes				goto failure;
1138135446Strhodes			}
1139135446Strhodes		} else {
1140135446Strhodes			rdataclass = getzoneclass();
1141135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1142135446Strhodes			if (result != ISC_R_SUCCESS) {
1143135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1144135446Strhodes				goto failure;
1145135446Strhodes			}
1146135446Strhodes		}
1147135446Strhodes	} else
1148135446Strhodes		rdatatype = dns_rdatatype_any;
1149135446Strhodes
1150135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1151135446Strhodes	check_result(result, "dns_message_gettemprdata");
1152135446Strhodes
1153193149Sdougb	dns_rdata_init(rdata);
1154135446Strhodes
1155135446Strhodes	if (isrrset && ispositive) {
1156135446Strhodes		retval = parse_rdata(&cmdline, rdataclass, rdatatype,
1157135446Strhodes				     updatemsg, rdata);
1158135446Strhodes		if (retval != STATUS_MORE)
1159135446Strhodes			goto failure;
1160135446Strhodes	} else
1161135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1162135446Strhodes
1163135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1164135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1165135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1166135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1167135446Strhodes	dns_rdatalist_init(rdatalist);
1168135446Strhodes	rdatalist->type = rdatatype;
1169135446Strhodes	if (ispositive) {
1170135446Strhodes		if (isrrset && rdata->data != NULL)
1171135446Strhodes			rdatalist->rdclass = rdataclass;
1172135446Strhodes		else
1173135446Strhodes			rdatalist->rdclass = dns_rdataclass_any;
1174135446Strhodes	} else
1175135446Strhodes		rdatalist->rdclass = dns_rdataclass_none;
1176135446Strhodes	rdatalist->covers = 0;
1177135446Strhodes	rdatalist->ttl = 0;
1178135446Strhodes	rdata->rdclass = rdatalist->rdclass;
1179135446Strhodes	rdata->type = rdatatype;
1180135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1181135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1182135446Strhodes	dns_rdataset_init(rdataset);
1183135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1184135446Strhodes	ISC_LIST_INIT(name->list);
1185135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1186135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_PREREQUISITE);
1187135446Strhodes	return (STATUS_MORE);
1188135446Strhodes
1189135446Strhodes failure:
1190135446Strhodes	if (name != NULL)
1191135446Strhodes		dns_message_puttempname(updatemsg, &name);
1192135446Strhodes	return (STATUS_SYNTAX);
1193135446Strhodes}
1194135446Strhodes
1195135446Strhodesstatic isc_uint16_t
1196135446Strhodesevaluate_prereq(char *cmdline) {
1197135446Strhodes	char *word;
1198135446Strhodes	isc_boolean_t ispositive, isrrset;
1199135446Strhodes
1200135446Strhodes	ddebug("evaluate_prereq()");
1201135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1202135446Strhodes	if (*word == 0) {
1203135446Strhodes		fprintf(stderr, "could not read operation code\n");
1204135446Strhodes		return (STATUS_SYNTAX);
1205135446Strhodes	}
1206135446Strhodes	if (strcasecmp(word, "nxdomain") == 0) {
1207135446Strhodes		ispositive = ISC_FALSE;
1208135446Strhodes		isrrset = ISC_FALSE;
1209135446Strhodes	} else if (strcasecmp(word, "yxdomain") == 0) {
1210135446Strhodes		ispositive = ISC_TRUE;
1211135446Strhodes		isrrset = ISC_FALSE;
1212135446Strhodes	} else if (strcasecmp(word, "nxrrset") == 0) {
1213135446Strhodes		ispositive = ISC_FALSE;
1214135446Strhodes		isrrset = ISC_TRUE;
1215135446Strhodes	} else if (strcasecmp(word, "yxrrset") == 0) {
1216135446Strhodes		ispositive = ISC_TRUE;
1217135446Strhodes		isrrset = ISC_TRUE;
1218135446Strhodes	} else {
1219135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1220135446Strhodes		return (STATUS_SYNTAX);
1221135446Strhodes	}
1222135446Strhodes	return (make_prereq(cmdline, ispositive, isrrset));
1223135446Strhodes}
1224135446Strhodes
1225135446Strhodesstatic isc_uint16_t
1226135446Strhodesevaluate_server(char *cmdline) {
1227135446Strhodes	char *word, *server;
1228135446Strhodes	long port;
1229135446Strhodes
1230135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1231135446Strhodes	if (*word == 0) {
1232135446Strhodes		fprintf(stderr, "could not read server name\n");
1233135446Strhodes		return (STATUS_SYNTAX);
1234135446Strhodes	}
1235135446Strhodes	server = word;
1236135446Strhodes
1237135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1238135446Strhodes	if (*word == 0)
1239135446Strhodes		port = DNSDEFAULTPORT;
1240135446Strhodes	else {
1241135446Strhodes		char *endp;
1242135446Strhodes		port = strtol(word, &endp, 10);
1243135446Strhodes		if (*endp != 0) {
1244135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1245135446Strhodes			return (STATUS_SYNTAX);
1246135446Strhodes		} else if (port < 1 || port > 65535) {
1247135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1248135446Strhodes				"(1 to 65535)\n", word);
1249135446Strhodes			return (STATUS_SYNTAX);
1250135446Strhodes		}
1251135446Strhodes	}
1252135446Strhodes
1253135446Strhodes	if (userserver == NULL) {
1254135446Strhodes		userserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1255135446Strhodes		if (userserver == NULL)
1256135446Strhodes			fatal("out of memory");
1257135446Strhodes	}
1258135446Strhodes
1259135446Strhodes	get_address(server, (in_port_t)port, userserver);
1260135446Strhodes
1261135446Strhodes	return (STATUS_MORE);
1262135446Strhodes}
1263135446Strhodes
1264135446Strhodesstatic isc_uint16_t
1265135446Strhodesevaluate_local(char *cmdline) {
1266135446Strhodes	char *word, *local;
1267135446Strhodes	long port;
1268135446Strhodes	struct in_addr in4;
1269135446Strhodes	struct in6_addr in6;
1270135446Strhodes
1271135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1272135446Strhodes	if (*word == 0) {
1273135446Strhodes		fprintf(stderr, "could not read server name\n");
1274135446Strhodes		return (STATUS_SYNTAX);
1275135446Strhodes	}
1276135446Strhodes	local = word;
1277135446Strhodes
1278135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1279135446Strhodes	if (*word == 0)
1280135446Strhodes		port = 0;
1281135446Strhodes	else {
1282135446Strhodes		char *endp;
1283135446Strhodes		port = strtol(word, &endp, 10);
1284135446Strhodes		if (*endp != 0) {
1285135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1286135446Strhodes			return (STATUS_SYNTAX);
1287135446Strhodes		} else if (port < 1 || port > 65535) {
1288135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1289135446Strhodes				"(1 to 65535)\n", word);
1290135446Strhodes			return (STATUS_SYNTAX);
1291135446Strhodes		}
1292135446Strhodes	}
1293135446Strhodes
1294135446Strhodes	if (localaddr == NULL) {
1295135446Strhodes		localaddr = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1296135446Strhodes		if (localaddr == NULL)
1297135446Strhodes			fatal("out of memory");
1298135446Strhodes	}
1299135446Strhodes
1300135446Strhodes	if (have_ipv6 && inet_pton(AF_INET6, local, &in6) == 1)
1301135446Strhodes		isc_sockaddr_fromin6(localaddr, &in6, (in_port_t)port);
1302135446Strhodes	else if (have_ipv4 && inet_pton(AF_INET, local, &in4) == 1)
1303135446Strhodes		isc_sockaddr_fromin(localaddr, &in4, (in_port_t)port);
1304135446Strhodes	else {
1305135446Strhodes		fprintf(stderr, "invalid address %s", local);
1306135446Strhodes		return (STATUS_SYNTAX);
1307135446Strhodes	}
1308135446Strhodes
1309135446Strhodes	return (STATUS_MORE);
1310135446Strhodes}
1311135446Strhodes
1312135446Strhodesstatic isc_uint16_t
1313135446Strhodesevaluate_key(char *cmdline) {
1314135446Strhodes	char *namestr;
1315135446Strhodes	char *secretstr;
1316135446Strhodes	isc_buffer_t b;
1317135446Strhodes	isc_result_t result;
1318135446Strhodes	dns_fixedname_t fkeyname;
1319135446Strhodes	dns_name_t *keyname;
1320135446Strhodes	int secretlen;
1321135446Strhodes	unsigned char *secret = NULL;
1322135446Strhodes	isc_buffer_t secretbuf;
1323170222Sdougb	dns_name_t *hmacname = NULL;
1324170222Sdougb	isc_uint16_t digestbits = 0;
1325170222Sdougb	char *n;
1326135446Strhodes
1327135446Strhodes	namestr = nsu_strsep(&cmdline, " \t\r\n");
1328135446Strhodes	if (*namestr == 0) {
1329135446Strhodes		fprintf(stderr, "could not read key name\n");
1330135446Strhodes		return (STATUS_SYNTAX);
1331135446Strhodes	}
1332135446Strhodes
1333135446Strhodes	dns_fixedname_init(&fkeyname);
1334135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
1335135446Strhodes
1336170222Sdougb	n = strchr(namestr, ':');
1337170222Sdougb	if (n != NULL) {
1338170222Sdougb		digestbits = parse_hmac(&hmacname, namestr, n - namestr);
1339170222Sdougb		namestr = n + 1;
1340170222Sdougb	} else
1341170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
1342170222Sdougb
1343135446Strhodes	isc_buffer_init(&b, namestr, strlen(namestr));
1344135446Strhodes	isc_buffer_add(&b, strlen(namestr));
1345135446Strhodes	result = dns_name_fromtext(keyname, &b, dns_rootname, ISC_FALSE, NULL);
1346135446Strhodes	if (result != ISC_R_SUCCESS) {
1347135446Strhodes		fprintf(stderr, "could not parse key name\n");
1348135446Strhodes		return (STATUS_SYNTAX);
1349135446Strhodes	}
1350135446Strhodes
1351135446Strhodes	secretstr = nsu_strsep(&cmdline, "\r\n");
1352135446Strhodes	if (*secretstr == 0) {
1353135446Strhodes		fprintf(stderr, "could not read key secret\n");
1354135446Strhodes		return (STATUS_SYNTAX);
1355135446Strhodes	}
1356135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
1357135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
1358135446Strhodes	if (secret == NULL)
1359135446Strhodes		fatal("out of memory");
1360186462Sdougb
1361135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
1362135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
1363135446Strhodes	if (result != ISC_R_SUCCESS) {
1364135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
1365135446Strhodes			secretstr, isc_result_totext(result));
1366135446Strhodes		isc_mem_free(mctx, secret);
1367135446Strhodes		return (STATUS_SYNTAX);
1368135446Strhodes	}
1369135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
1370135446Strhodes
1371135446Strhodes	if (tsigkey != NULL)
1372135446Strhodes		dns_tsigkey_detach(&tsigkey);
1373170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
1374218384Sdougb				    ISC_FALSE, NULL, 0, 0, mctx, NULL,
1375170222Sdougb				    &tsigkey);
1376135446Strhodes	isc_mem_free(mctx, secret);
1377135446Strhodes	if (result != ISC_R_SUCCESS) {
1378135446Strhodes		fprintf(stderr, "could not create key from %s %s: %s\n",
1379135446Strhodes			namestr, secretstr, dns_result_totext(result));
1380135446Strhodes		return (STATUS_SYNTAX);
1381135446Strhodes	}
1382170222Sdougb	dst_key_setbits(tsigkey->key, digestbits);
1383135446Strhodes	return (STATUS_MORE);
1384135446Strhodes}
1385135446Strhodes
1386135446Strhodesstatic isc_uint16_t
1387135446Strhodesevaluate_zone(char *cmdline) {
1388135446Strhodes	char *word;
1389135446Strhodes	isc_buffer_t b;
1390135446Strhodes	isc_result_t result;
1391135446Strhodes
1392135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1393135446Strhodes	if (*word == 0) {
1394135446Strhodes		fprintf(stderr, "could not read zone name\n");
1395135446Strhodes		return (STATUS_SYNTAX);
1396135446Strhodes	}
1397135446Strhodes
1398135446Strhodes	dns_fixedname_init(&fuserzone);
1399135446Strhodes	userzone = dns_fixedname_name(&fuserzone);
1400135446Strhodes	isc_buffer_init(&b, word, strlen(word));
1401135446Strhodes	isc_buffer_add(&b, strlen(word));
1402135446Strhodes	result = dns_name_fromtext(userzone, &b, dns_rootname, ISC_FALSE,
1403135446Strhodes				   NULL);
1404135446Strhodes	if (result != ISC_R_SUCCESS) {
1405135446Strhodes		userzone = NULL; /* Lest it point to an invalid name */
1406135446Strhodes		fprintf(stderr, "could not parse zone name\n");
1407135446Strhodes		return (STATUS_SYNTAX);
1408135446Strhodes	}
1409135446Strhodes
1410135446Strhodes	return (STATUS_MORE);
1411135446Strhodes}
1412135446Strhodes
1413135446Strhodesstatic isc_uint16_t
1414218384Sdougbevaluate_realm(char *cmdline) {
1415218384Sdougb#ifdef GSSAPI
1416218384Sdougb	char *word;
1417218384Sdougb	char buf[1024];
1418218384Sdougb
1419218384Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
1420218384Sdougb	if (*word == 0) {
1421218384Sdougb		if (realm != NULL)
1422218384Sdougb			isc_mem_free(mctx, realm);
1423218384Sdougb		realm = NULL;
1424218384Sdougb		return (STATUS_MORE);
1425218384Sdougb	}
1426218384Sdougb
1427218384Sdougb	snprintf(buf, sizeof(buf), "@%s", word);
1428218384Sdougb	realm = isc_mem_strdup(mctx, buf);
1429218384Sdougb	if (realm == NULL)
1430218384Sdougb		fatal("out of memory");
1431218384Sdougb	return (STATUS_MORE);
1432218384Sdougb#else
1433218384Sdougb	UNUSED(cmdline);
1434218384Sdougb	return (STATUS_SYNTAX);
1435218384Sdougb#endif
1436218384Sdougb}
1437218384Sdougb
1438218384Sdougbstatic isc_uint16_t
1439193149Sdougbevaluate_ttl(char *cmdline) {
1440193149Sdougb	char *word;
1441193149Sdougb	isc_result_t result;
1442193149Sdougb	isc_uint32_t ttl;
1443193149Sdougb
1444193149Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
1445193149Sdougb	if (*word == 0) {
1446193149Sdougb		fprintf(stderr, "could not ttl\n");
1447193149Sdougb		return (STATUS_SYNTAX);
1448193149Sdougb	}
1449193149Sdougb
1450193149Sdougb	if (!strcasecmp(word, "none")) {
1451193149Sdougb		default_ttl = 0;
1452193149Sdougb		default_ttl_set = ISC_FALSE;
1453193149Sdougb		return (STATUS_MORE);
1454193149Sdougb	}
1455193149Sdougb
1456193149Sdougb	result = isc_parse_uint32(&ttl, word, 10);
1457193149Sdougb	if (result != ISC_R_SUCCESS)
1458193149Sdougb		return (STATUS_SYNTAX);
1459193149Sdougb
1460193149Sdougb	if (ttl > TTL_MAX) {
1461193149Sdougb		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1462193149Sdougb			word, TTL_MAX);
1463193149Sdougb		return (STATUS_SYNTAX);
1464193149Sdougb	}
1465193149Sdougb	default_ttl = ttl;
1466193149Sdougb	default_ttl_set = ISC_TRUE;
1467193149Sdougb
1468193149Sdougb	return (STATUS_MORE);
1469193149Sdougb}
1470193149Sdougb
1471193149Sdougbstatic isc_uint16_t
1472135446Strhodesevaluate_class(char *cmdline) {
1473135446Strhodes	char *word;
1474135446Strhodes	isc_textregion_t r;
1475135446Strhodes	isc_result_t result;
1476135446Strhodes	dns_rdataclass_t rdclass;
1477135446Strhodes
1478135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1479135446Strhodes	if (*word == 0) {
1480135446Strhodes		fprintf(stderr, "could not read class name\n");
1481135446Strhodes		return (STATUS_SYNTAX);
1482135446Strhodes	}
1483135446Strhodes
1484135446Strhodes	r.base = word;
1485186462Sdougb	r.length = strlen(word);
1486186462Sdougb	result = dns_rdataclass_fromtext(&rdclass, &r);
1487135446Strhodes	if (result != ISC_R_SUCCESS) {
1488135446Strhodes		fprintf(stderr, "could not parse class name: %s\n", word);
1489135446Strhodes		return (STATUS_SYNTAX);
1490135446Strhodes	}
1491135446Strhodes	switch (rdclass) {
1492135446Strhodes	case dns_rdataclass_none:
1493135446Strhodes	case dns_rdataclass_any:
1494135446Strhodes	case dns_rdataclass_reserved0:
1495135446Strhodes		fprintf(stderr, "bad default class: %s\n", word);
1496135446Strhodes		return (STATUS_SYNTAX);
1497135446Strhodes	default:
1498135446Strhodes		defaultclass = rdclass;
1499135446Strhodes	}
1500135446Strhodes
1501135446Strhodes	return (STATUS_MORE);
1502135446Strhodes}
1503135446Strhodes
1504135446Strhodesstatic isc_uint16_t
1505135446Strhodesupdate_addordelete(char *cmdline, isc_boolean_t isdelete) {
1506135446Strhodes	isc_result_t result;
1507135446Strhodes	dns_name_t *name = NULL;
1508135446Strhodes	isc_uint32_t ttl;
1509135446Strhodes	char *word;
1510135446Strhodes	dns_rdataclass_t rdataclass;
1511135446Strhodes	dns_rdatatype_t rdatatype;
1512135446Strhodes	dns_rdata_t *rdata = NULL;
1513135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1514135446Strhodes	dns_rdataset_t *rdataset = NULL;
1515135446Strhodes	isc_textregion_t region;
1516135446Strhodes	isc_uint16_t retval;
1517135446Strhodes
1518135446Strhodes	ddebug("update_addordelete()");
1519135446Strhodes
1520135446Strhodes	/*
1521135446Strhodes	 * Read the owner name.
1522135446Strhodes	 */
1523135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1524135446Strhodes	if (retval != STATUS_MORE)
1525135446Strhodes		return (retval);
1526135446Strhodes
1527135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1528135446Strhodes	check_result(result, "dns_message_gettemprdata");
1529135446Strhodes
1530193149Sdougb	dns_rdata_init(rdata);
1531135446Strhodes
1532135446Strhodes	/*
1533135446Strhodes	 * If this is an add, read the TTL and verify that it's in range.
1534135446Strhodes	 * If it's a delete, ignore a TTL if present (for compatibility).
1535135446Strhodes	 */
1536135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1537135446Strhodes	if (*word == 0) {
1538135446Strhodes		if (!isdelete) {
1539135446Strhodes			fprintf(stderr, "could not read owner ttl\n");
1540135446Strhodes			goto failure;
1541135446Strhodes		}
1542135446Strhodes		else {
1543135446Strhodes			ttl = 0;
1544135446Strhodes			rdataclass = dns_rdataclass_any;
1545135446Strhodes			rdatatype = dns_rdatatype_any;
1546135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1547135446Strhodes			goto doneparsing;
1548135446Strhodes		}
1549135446Strhodes	}
1550135446Strhodes	result = isc_parse_uint32(&ttl, word, 10);
1551135446Strhodes	if (result != ISC_R_SUCCESS) {
1552135446Strhodes		if (isdelete) {
1553135446Strhodes			ttl = 0;
1554135446Strhodes			goto parseclass;
1555193149Sdougb		} else if (default_ttl_set) {
1556193149Sdougb			ttl = default_ttl;
1557193149Sdougb			goto parseclass;
1558135446Strhodes		} else {
1559135446Strhodes			fprintf(stderr, "ttl '%s': %s\n", word,
1560135446Strhodes				isc_result_totext(result));
1561135446Strhodes			goto failure;
1562135446Strhodes		}
1563135446Strhodes	}
1564135446Strhodes
1565135446Strhodes	if (isdelete)
1566135446Strhodes		ttl = 0;
1567135446Strhodes	else if (ttl > TTL_MAX) {
1568135446Strhodes		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1569135446Strhodes			word, TTL_MAX);
1570135446Strhodes		goto failure;
1571135446Strhodes	}
1572135446Strhodes
1573135446Strhodes	/*
1574135446Strhodes	 * Read the class or type.
1575135446Strhodes	 */
1576135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1577135446Strhodes parseclass:
1578135446Strhodes	if (*word == 0) {
1579135446Strhodes		if (isdelete) {
1580135446Strhodes			rdataclass = dns_rdataclass_any;
1581135446Strhodes			rdatatype = dns_rdatatype_any;
1582135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1583135446Strhodes			goto doneparsing;
1584135446Strhodes		} else {
1585135446Strhodes			fprintf(stderr, "could not read class or type\n");
1586135446Strhodes			goto failure;
1587135446Strhodes		}
1588135446Strhodes	}
1589135446Strhodes	region.base = word;
1590135446Strhodes	region.length = strlen(word);
1591193149Sdougb	rdataclass = dns_rdataclass_any;
1592135446Strhodes	result = dns_rdataclass_fromtext(&rdataclass, &region);
1593193149Sdougb	if (result == ISC_R_SUCCESS && rdataclass != dns_rdataclass_any) {
1594135446Strhodes		if (!setzoneclass(rdataclass)) {
1595135446Strhodes			fprintf(stderr, "class mismatch: %s\n", word);
1596135446Strhodes			goto failure;
1597135446Strhodes		}
1598135446Strhodes		/*
1599135446Strhodes		 * Now read the type.
1600135446Strhodes		 */
1601135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1602135446Strhodes		if (*word == 0) {
1603135446Strhodes			if (isdelete) {
1604135446Strhodes				rdataclass = dns_rdataclass_any;
1605135446Strhodes				rdatatype = dns_rdatatype_any;
1606135446Strhodes				rdata->flags = DNS_RDATA_UPDATE;
1607135446Strhodes				goto doneparsing;
1608135446Strhodes			} else {
1609135446Strhodes				fprintf(stderr, "could not read type\n");
1610135446Strhodes				goto failure;
1611135446Strhodes			}
1612135446Strhodes		}
1613135446Strhodes		region.base = word;
1614135446Strhodes		region.length = strlen(word);
1615135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1616135446Strhodes		if (result != ISC_R_SUCCESS) {
1617135446Strhodes			fprintf(stderr, "'%s' is not a valid type: %s\n",
1618135446Strhodes				word, isc_result_totext(result));
1619135446Strhodes			goto failure;
1620135446Strhodes		}
1621135446Strhodes	} else {
1622135446Strhodes		rdataclass = getzoneclass();
1623135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1624135446Strhodes		if (result != ISC_R_SUCCESS) {
1625135446Strhodes			fprintf(stderr, "'%s' is not a valid class or type: "
1626135446Strhodes				"%s\n", word, isc_result_totext(result));
1627135446Strhodes			goto failure;
1628135446Strhodes		}
1629135446Strhodes	}
1630135446Strhodes
1631135446Strhodes	retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg,
1632135446Strhodes			     rdata);
1633135446Strhodes	if (retval != STATUS_MORE)
1634135446Strhodes		goto failure;
1635135446Strhodes
1636135446Strhodes	if (isdelete) {
1637135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
1638135446Strhodes			rdataclass = dns_rdataclass_any;
1639135446Strhodes		else
1640135446Strhodes			rdataclass = dns_rdataclass_none;
1641135446Strhodes	} else {
1642135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
1643135446Strhodes			fprintf(stderr, "could not read rdata\n");
1644135446Strhodes			goto failure;
1645135446Strhodes		}
1646135446Strhodes	}
1647135446Strhodes
1648135446Strhodes doneparsing:
1649135446Strhodes
1650135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1651135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1652135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1653135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1654135446Strhodes	dns_rdatalist_init(rdatalist);
1655135446Strhodes	rdatalist->type = rdatatype;
1656135446Strhodes	rdatalist->rdclass = rdataclass;
1657135446Strhodes	rdatalist->covers = rdatatype;
1658135446Strhodes	rdatalist->ttl = (dns_ttl_t)ttl;
1659135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1660135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1661135446Strhodes	dns_rdataset_init(rdataset);
1662135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1663135446Strhodes	ISC_LIST_INIT(name->list);
1664135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1665135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_UPDATE);
1666135446Strhodes	return (STATUS_MORE);
1667135446Strhodes
1668135446Strhodes failure:
1669135446Strhodes	if (name != NULL)
1670135446Strhodes		dns_message_puttempname(updatemsg, &name);
1671186462Sdougb	dns_message_puttemprdata(updatemsg, &rdata);
1672135446Strhodes	return (STATUS_SYNTAX);
1673135446Strhodes}
1674135446Strhodes
1675135446Strhodesstatic isc_uint16_t
1676135446Strhodesevaluate_update(char *cmdline) {
1677135446Strhodes	char *word;
1678135446Strhodes	isc_boolean_t isdelete;
1679135446Strhodes
1680135446Strhodes	ddebug("evaluate_update()");
1681135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1682135446Strhodes	if (*word == 0) {
1683135446Strhodes		fprintf(stderr, "could not read operation code\n");
1684135446Strhodes		return (STATUS_SYNTAX);
1685135446Strhodes	}
1686135446Strhodes	if (strcasecmp(word, "delete") == 0)
1687135446Strhodes		isdelete = ISC_TRUE;
1688135446Strhodes	else if (strcasecmp(word, "add") == 0)
1689135446Strhodes		isdelete = ISC_FALSE;
1690135446Strhodes	else {
1691135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1692135446Strhodes		return (STATUS_SYNTAX);
1693135446Strhodes	}
1694135446Strhodes	return (update_addordelete(cmdline, isdelete));
1695135446Strhodes}
1696135446Strhodes
1697135446Strhodesstatic void
1698170222Sdougbsetzone(dns_name_t *zonename) {
1699170222Sdougb	isc_result_t result;
1700170222Sdougb	dns_name_t *name = NULL;
1701170222Sdougb	dns_rdataset_t *rdataset = NULL;
1702170222Sdougb
1703170222Sdougb	result = dns_message_firstname(updatemsg, DNS_SECTION_ZONE);
1704170222Sdougb	if (result == ISC_R_SUCCESS) {
1705170222Sdougb		dns_message_currentname(updatemsg, DNS_SECTION_ZONE, &name);
1706170222Sdougb		dns_message_removename(updatemsg, name, DNS_SECTION_ZONE);
1707170222Sdougb		for (rdataset = ISC_LIST_HEAD(name->list);
1708170222Sdougb		     rdataset != NULL;
1709170222Sdougb		     rdataset = ISC_LIST_HEAD(name->list)) {
1710170222Sdougb			ISC_LIST_UNLINK(name->list, rdataset, link);
1711170222Sdougb			dns_rdataset_disassociate(rdataset);
1712170222Sdougb			dns_message_puttemprdataset(updatemsg, &rdataset);
1713170222Sdougb		}
1714170222Sdougb		dns_message_puttempname(updatemsg, &name);
1715170222Sdougb	}
1716170222Sdougb
1717170222Sdougb	if (zonename != NULL) {
1718170222Sdougb		result = dns_message_gettempname(updatemsg, &name);
1719170222Sdougb		check_result(result, "dns_message_gettempname");
1720170222Sdougb		dns_name_init(name, NULL);
1721170222Sdougb		dns_name_clone(zonename, name);
1722170222Sdougb		result = dns_message_gettemprdataset(updatemsg, &rdataset);
1723170222Sdougb		check_result(result, "dns_message_gettemprdataset");
1724170222Sdougb		dns_rdataset_makequestion(rdataset, getzoneclass(),
1725170222Sdougb					  dns_rdatatype_soa);
1726170222Sdougb		ISC_LIST_INIT(name->list);
1727170222Sdougb		ISC_LIST_APPEND(name->list, rdataset, link);
1728170222Sdougb		dns_message_addname(updatemsg, name, DNS_SECTION_ZONE);
1729170222Sdougb	}
1730170222Sdougb}
1731170222Sdougb
1732170222Sdougbstatic void
1733193149Sdougbshow_message(FILE *stream, dns_message_t *msg, const char *description) {
1734135446Strhodes	isc_result_t result;
1735135446Strhodes	isc_buffer_t *buf = NULL;
1736135446Strhodes	int bufsz;
1737135446Strhodes
1738135446Strhodes	ddebug("show_message()");
1739170222Sdougb
1740170222Sdougb	setzone(userzone);
1741170222Sdougb
1742135446Strhodes	bufsz = INITTEXT;
1743186462Sdougb	do {
1744135446Strhodes		if (bufsz > MAXTEXT) {
1745135446Strhodes			fprintf(stderr, "could not allocate large enough "
1746135446Strhodes				"buffer to display message\n");
1747135446Strhodes			exit(1);
1748135446Strhodes		}
1749135446Strhodes		if (buf != NULL)
1750135446Strhodes			isc_buffer_free(&buf);
1751135446Strhodes		result = isc_buffer_allocate(mctx, &buf, bufsz);
1752135446Strhodes		check_result(result, "isc_buffer_allocate");
1753135446Strhodes		result = dns_message_totext(msg, style, 0, buf);
1754135446Strhodes		bufsz *= 2;
1755135446Strhodes	} while (result == ISC_R_NOSPACE);
1756135446Strhodes	if (result != ISC_R_SUCCESS) {
1757135446Strhodes		fprintf(stderr, "could not convert message to text format.\n");
1758135446Strhodes		isc_buffer_free(&buf);
1759135446Strhodes		return;
1760135446Strhodes	}
1761193149Sdougb	fprintf(stream, "%s\n%.*s", description,
1762193149Sdougb	       (int)isc_buffer_usedlength(buf), (char*)isc_buffer_base(buf));
1763135446Strhodes	isc_buffer_free(&buf);
1764135446Strhodes}
1765135446Strhodes
1766135446Strhodes
1767135446Strhodesstatic isc_uint16_t
1768135446Strhodesget_next_command(void) {
1769135446Strhodes	char cmdlinebuf[MAXCMD];
1770135446Strhodes	char *cmdline;
1771135446Strhodes	char *word;
1772135446Strhodes
1773135446Strhodes	ddebug("get_next_command()");
1774165071Sdougb	if (interactive) {
1775135446Strhodes		fprintf(stdout, "> ");
1776165071Sdougb		fflush(stdout);
1777165071Sdougb	}
1778135446Strhodes	isc_app_block();
1779135446Strhodes	cmdline = fgets(cmdlinebuf, MAXCMD, input);
1780135446Strhodes	isc_app_unblock();
1781135446Strhodes	if (cmdline == NULL)
1782135446Strhodes		return (STATUS_QUIT);
1783135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1784135446Strhodes
1785135446Strhodes	if (feof(input))
1786135446Strhodes		return (STATUS_QUIT);
1787135446Strhodes	if (*word == 0)
1788135446Strhodes		return (STATUS_SEND);
1789135446Strhodes	if (word[0] == ';')
1790135446Strhodes		return (STATUS_MORE);
1791135446Strhodes	if (strcasecmp(word, "quit") == 0)
1792135446Strhodes		return (STATUS_QUIT);
1793135446Strhodes	if (strcasecmp(word, "prereq") == 0)
1794135446Strhodes		return (evaluate_prereq(cmdline));
1795135446Strhodes	if (strcasecmp(word, "update") == 0)
1796135446Strhodes		return (evaluate_update(cmdline));
1797135446Strhodes	if (strcasecmp(word, "server") == 0)
1798135446Strhodes		return (evaluate_server(cmdline));
1799135446Strhodes	if (strcasecmp(word, "local") == 0)
1800135446Strhodes		return (evaluate_local(cmdline));
1801135446Strhodes	if (strcasecmp(word, "zone") == 0)
1802135446Strhodes		return (evaluate_zone(cmdline));
1803135446Strhodes	if (strcasecmp(word, "class") == 0)
1804135446Strhodes		return (evaluate_class(cmdline));
1805135446Strhodes	if (strcasecmp(word, "send") == 0)
1806135446Strhodes		return (STATUS_SEND);
1807193149Sdougb	if (strcasecmp(word, "debug") == 0) {
1808193149Sdougb		if (debugging)
1809193149Sdougb			ddebugging = ISC_TRUE;
1810193149Sdougb		else
1811193149Sdougb			debugging = ISC_TRUE;
1812193149Sdougb		return (STATUS_MORE);
1813193149Sdougb	}
1814193149Sdougb	if (strcasecmp(word, "ttl") == 0)
1815193149Sdougb		return (evaluate_ttl(cmdline));
1816135446Strhodes	if (strcasecmp(word, "show") == 0) {
1817193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
1818135446Strhodes		return (STATUS_MORE);
1819135446Strhodes	}
1820135446Strhodes	if (strcasecmp(word, "answer") == 0) {
1821135446Strhodes		if (answer != NULL)
1822193149Sdougb			show_message(stdout, answer, "Answer:");
1823135446Strhodes		return (STATUS_MORE);
1824135446Strhodes	}
1825193149Sdougb	if (strcasecmp(word, "key") == 0) {
1826193149Sdougb		usegsstsig = ISC_FALSE;
1827135446Strhodes		return (evaluate_key(cmdline));
1828193149Sdougb	}
1829218384Sdougb	if (strcasecmp(word, "realm") == 0)
1830218384Sdougb		return (evaluate_realm(cmdline));
1831193149Sdougb	if (strcasecmp(word, "gsstsig") == 0) {
1832193149Sdougb#ifdef GSSAPI
1833193149Sdougb		usegsstsig = ISC_TRUE;
1834193149Sdougb		use_win2k_gsstsig = ISC_FALSE;
1835193149Sdougb#else
1836193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1837193149Sdougb#endif
1838193149Sdougb		return (STATUS_MORE);
1839193149Sdougb	}
1840193149Sdougb	if (strcasecmp(word, "oldgsstsig") == 0) {
1841193149Sdougb#ifdef GSSAPI
1842193149Sdougb		usegsstsig = ISC_TRUE;
1843193149Sdougb		use_win2k_gsstsig = ISC_TRUE;
1844193149Sdougb#else
1845193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1846193149Sdougb#endif
1847193149Sdougb		return (STATUS_MORE);
1848193149Sdougb	}
1849193149Sdougb	if (strcasecmp(word, "help") == 0) {
1850193149Sdougb		fprintf(stdout,
1851193149Sdougb"local address [port]      (set local resolver)\n"
1852193149Sdougb"server address [port]     (set master server for zone)\n"
1853193149Sdougb"send                      (send the update request)\n"
1854193149Sdougb"show                      (show the update request)\n"
1855193149Sdougb"answer	                   (show the answer to the last request)\n"
1856193149Sdougb"quit                      (quit, any pending update is not sent\n"
1857193149Sdougb"help			   (display this message_\n"
1858193149Sdougb"key [hmac:]keyname secret (use TSIG to sign the request)\n"
1859193149Sdougb"gsstsig                   (use GSS_TSIG to sign the request)\n"
1860193149Sdougb"oldgsstsig                (use Microsoft's GSS_TSIG to sign the request)\n"
1861193149Sdougb"zone name                 (set the zone to be updated)\n"
1862193149Sdougb"class CLASS               (set the zone's DNS class, e.g. IN (default), CH)\n"
1863193149Sdougb"prereq nxdomain name      (does this name not exist)\n"
1864193149Sdougb"prereq yxdomain name      (does this name exist)\n"
1865193149Sdougb"prereq nxrrset ....       (does this RRset exist)\n"
1866193149Sdougb"prereq yxrrset ....       (does this RRset not exist)\n"
1867193149Sdougb"update add ....           (add the given record to the zone)\n"
1868193149Sdougb"update delete ....        (remove the given record(s) from the zone)\n");
1869193149Sdougb		return (STATUS_MORE);
1870193149Sdougb	}
1871135446Strhodes	fprintf(stderr, "incorrect section name: %s\n", word);
1872135446Strhodes	return (STATUS_SYNTAX);
1873135446Strhodes}
1874135446Strhodes
1875135446Strhodesstatic isc_boolean_t
1876135446Strhodesuser_interaction(void) {
1877135446Strhodes	isc_uint16_t result = STATUS_MORE;
1878135446Strhodes
1879135446Strhodes	ddebug("user_interaction()");
1880174187Sdougb	while ((result == STATUS_MORE) || (result == STATUS_SYNTAX)) {
1881135446Strhodes		result = get_next_command();
1882174187Sdougb		if (!interactive && result == STATUS_SYNTAX)
1883174187Sdougb			fatal("syntax error");
1884174187Sdougb	}
1885135446Strhodes	if (result == STATUS_SEND)
1886135446Strhodes		return (ISC_TRUE);
1887135446Strhodes	return (ISC_FALSE);
1888135446Strhodes
1889135446Strhodes}
1890135446Strhodes
1891135446Strhodesstatic void
1892135446Strhodesdone_update(void) {
1893135446Strhodes	isc_event_t *event = global_event;
1894135446Strhodes	ddebug("done_update()");
1895135446Strhodes	isc_task_send(global_task, &event);
1896135446Strhodes}
1897135446Strhodes
1898135446Strhodesstatic void
1899135446Strhodescheck_tsig_error(dns_rdataset_t *rdataset, isc_buffer_t *b) {
1900135446Strhodes	isc_result_t result;
1901135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1902135446Strhodes	dns_rdata_any_tsig_t tsig;
1903135446Strhodes
1904135446Strhodes	result = dns_rdataset_first(rdataset);
1905135446Strhodes	check_result(result, "dns_rdataset_first");
1906135446Strhodes	dns_rdataset_current(rdataset, &rdata);
1907135446Strhodes	result = dns_rdata_tostruct(&rdata, &tsig, NULL);
1908135446Strhodes	check_result(result, "dns_rdata_tostruct");
1909135446Strhodes	if (tsig.error != 0) {
1910135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
1911135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
1912135446Strhodes		isc__buffer_putstr(b, "(" /*)*/);
1913135446Strhodes		result = dns_tsigrcode_totext(tsig.error, b);
1914135446Strhodes		check_result(result, "dns_tsigrcode_totext");
1915135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
1916135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
1917135446Strhodes		isc__buffer_putstr(b,  /*(*/ ")");
1918135446Strhodes	}
1919135446Strhodes}
1920135446Strhodes
1921135446Strhodesstatic void
1922135446Strhodesupdate_completed(isc_task_t *task, isc_event_t *event) {
1923135446Strhodes	dns_requestevent_t *reqev = NULL;
1924135446Strhodes	isc_result_t result;
1925135446Strhodes	dns_request_t *request;
1926135446Strhodes
1927135446Strhodes	UNUSED(task);
1928135446Strhodes
1929135446Strhodes	ddebug("update_completed()");
1930135446Strhodes
1931135446Strhodes	requests--;
1932135446Strhodes
1933135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
1934135446Strhodes	reqev = (dns_requestevent_t *)event;
1935135446Strhodes	request = reqev->request;
1936135446Strhodes
1937135446Strhodes	if (shuttingdown) {
1938135446Strhodes		dns_request_destroy(&request);
1939135446Strhodes		isc_event_free(&event);
1940135446Strhodes		maybeshutdown();
1941135446Strhodes		return;
1942135446Strhodes	}
1943135446Strhodes
1944135446Strhodes	if (reqev->result != ISC_R_SUCCESS) {
1945135446Strhodes		fprintf(stderr, "; Communication with server failed: %s\n",
1946135446Strhodes			isc_result_totext(reqev->result));
1947135446Strhodes		seenerror = ISC_TRUE;
1948135446Strhodes		goto done;
1949135446Strhodes	}
1950135446Strhodes
1951135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &answer);
1952135446Strhodes	check_result(result, "dns_message_create");
1953135446Strhodes	result = dns_request_getresponse(request, answer,
1954135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
1955135446Strhodes	switch (result) {
1956135446Strhodes	case ISC_R_SUCCESS:
1957193149Sdougb		if (answer->verify_attempted)
1958193149Sdougb			ddebug("tsig verification successful");
1959135446Strhodes		break;
1960135446Strhodes	case DNS_R_CLOCKSKEW:
1961135446Strhodes	case DNS_R_EXPECTEDTSIG:
1962135446Strhodes	case DNS_R_TSIGERRORSET:
1963135446Strhodes	case DNS_R_TSIGVERIFYFAILURE:
1964135446Strhodes	case DNS_R_UNEXPECTEDTSIG:
1965193149Sdougb	case ISC_R_FAILURE:
1966193149Sdougb#if 0
1967193149Sdougb		if (usegsstsig && answer->rcode == dns_rcode_noerror) {
1968193149Sdougb			/*
1969193149Sdougb			 * For MS DNS that violates RFC 2845, section 4.2
1970193149Sdougb			 */
1971193149Sdougb			break;
1972193149Sdougb		}
1973193149Sdougb#endif
1974135446Strhodes		fprintf(stderr, "; TSIG error with server: %s\n",
1975135446Strhodes			isc_result_totext(result));
1976135446Strhodes		seenerror = ISC_TRUE;
1977135446Strhodes		break;
1978135446Strhodes	default:
1979135446Strhodes		check_result(result, "dns_request_getresponse");
1980135446Strhodes	}
1981135446Strhodes
1982135446Strhodes	if (answer->rcode != dns_rcode_noerror) {
1983135446Strhodes		seenerror = ISC_TRUE;
1984135446Strhodes		if (!debugging) {
1985135446Strhodes			char buf[64];
1986135446Strhodes			isc_buffer_t b;
1987135446Strhodes			dns_rdataset_t *rds;
1988186462Sdougb
1989135446Strhodes			isc_buffer_init(&b, buf, sizeof(buf) - 1);
1990135446Strhodes			result = dns_rcode_totext(answer->rcode, &b);
1991135446Strhodes			check_result(result, "dns_rcode_totext");
1992135446Strhodes			rds = dns_message_gettsig(answer, NULL);
1993135446Strhodes			if (rds != NULL)
1994135446Strhodes				check_tsig_error(rds, &b);
1995135446Strhodes			fprintf(stderr, "update failed: %.*s\n",
1996135446Strhodes				(int)isc_buffer_usedlength(&b), buf);
1997135446Strhodes		}
1998135446Strhodes	}
1999193149Sdougb	if (debugging)
2000193149Sdougb		show_message(stderr, answer, "\nReply from update query:");
2001135446Strhodes
2002135446Strhodes done:
2003135446Strhodes	dns_request_destroy(&request);
2004193149Sdougb	if (usegsstsig) {
2005193149Sdougb		dns_name_free(&tmpzonename, mctx);
2006193149Sdougb		dns_name_free(&restart_master, mctx);
2007193149Sdougb	}
2008135446Strhodes	isc_event_free(&event);
2009135446Strhodes	done_update();
2010135446Strhodes}
2011135446Strhodes
2012135446Strhodesstatic void
2013135446Strhodessend_update(dns_name_t *zonename, isc_sockaddr_t *master,
2014135446Strhodes	    isc_sockaddr_t *srcaddr)
2015135446Strhodes{
2016135446Strhodes	isc_result_t result;
2017135446Strhodes	dns_request_t *request = NULL;
2018135446Strhodes	unsigned int options = 0;
2019135446Strhodes
2020135446Strhodes	ddebug("send_update()");
2021135446Strhodes
2022170222Sdougb	setzone(zonename);
2023135446Strhodes
2024135446Strhodes	if (usevc)
2025135446Strhodes		options |= DNS_REQUESTOPT_TCP;
2026135446Strhodes	if (tsigkey == NULL && sig0key != NULL) {
2027135446Strhodes		result = dns_message_setsig0key(updatemsg, sig0key);
2028135446Strhodes		check_result(result, "dns_message_setsig0key");
2029135446Strhodes	}
2030135446Strhodes	if (debugging) {
2031135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2032135446Strhodes
2033135446Strhodes		isc_sockaddr_format(master, addrbuf, sizeof(addrbuf));
2034135446Strhodes		fprintf(stderr, "Sending update to %s\n", addrbuf);
2035135446Strhodes	}
2036193149Sdougb
2037218384Sdougb	/* Windows doesn't like the tsig name to be compressed. */
2038218384Sdougb	if (updatemsg->tsigname)
2039218384Sdougb		updatemsg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2040218384Sdougb
2041135446Strhodes	result = dns_request_createvia3(requestmgr, updatemsg, srcaddr,
2042135446Strhodes					master, options, tsigkey, timeout,
2043135446Strhodes					udp_timeout, udp_retries, global_task,
2044135446Strhodes					update_completed, NULL, &request);
2045135446Strhodes	check_result(result, "dns_request_createvia3");
2046135446Strhodes
2047135446Strhodes	if (debugging)
2048193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
2049135446Strhodes
2050135446Strhodes	requests++;
2051135446Strhodes}
2052135446Strhodes
2053135446Strhodesstatic void
2054135446Strhodesrecvsoa(isc_task_t *task, isc_event_t *event) {
2055135446Strhodes	dns_requestevent_t *reqev = NULL;
2056135446Strhodes	dns_request_t *request = NULL;
2057135446Strhodes	isc_result_t result, eresult;
2058135446Strhodes	dns_message_t *rcvmsg = NULL;
2059135446Strhodes	dns_section_t section;
2060135446Strhodes	dns_name_t *name = NULL;
2061135446Strhodes	dns_rdataset_t *soaset = NULL;
2062135446Strhodes	dns_rdata_soa_t soa;
2063135446Strhodes	dns_rdata_t soarr = DNS_RDATA_INIT;
2064135446Strhodes	int pass = 0;
2065135446Strhodes	dns_name_t master;
2066135446Strhodes	nsu_requestinfo_t *reqinfo;
2067135446Strhodes	dns_message_t *soaquery = NULL;
2068135446Strhodes	isc_sockaddr_t *addr;
2069135446Strhodes	isc_boolean_t seencname = ISC_FALSE;
2070143731Sdougb	dns_name_t tname;
2071143731Sdougb	unsigned int nlabels;
2072135446Strhodes
2073135446Strhodes	UNUSED(task);
2074135446Strhodes
2075135446Strhodes	ddebug("recvsoa()");
2076135446Strhodes
2077135446Strhodes	requests--;
2078186462Sdougb
2079135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2080135446Strhodes	reqev = (dns_requestevent_t *)event;
2081135446Strhodes	request = reqev->request;
2082135446Strhodes	eresult = reqev->result;
2083135446Strhodes	reqinfo = reqev->ev_arg;
2084135446Strhodes	soaquery = reqinfo->msg;
2085135446Strhodes	addr = reqinfo->addr;
2086135446Strhodes
2087135446Strhodes	if (shuttingdown) {
2088135446Strhodes		dns_request_destroy(&request);
2089135446Strhodes		dns_message_destroy(&soaquery);
2090135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2091135446Strhodes		isc_event_free(&event);
2092135446Strhodes		maybeshutdown();
2093135446Strhodes		return;
2094135446Strhodes	}
2095135446Strhodes
2096135446Strhodes	if (eresult != ISC_R_SUCCESS) {
2097135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2098135446Strhodes
2099135446Strhodes		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2100135446Strhodes		fprintf(stderr, "; Communication with %s failed: %s\n",
2101193149Sdougb			addrbuf, isc_result_totext(eresult));
2102135446Strhodes		if (userserver != NULL)
2103135446Strhodes			fatal("could not talk to specified name server");
2104135446Strhodes		else if (++ns_inuse >= lwconf->nsnext)
2105135446Strhodes			fatal("could not talk to any default name server");
2106135446Strhodes		ddebug("Destroying request [%p]", request);
2107135446Strhodes		dns_request_destroy(&request);
2108135446Strhodes		dns_message_renderreset(soaquery);
2109153816Sdougb		dns_message_settsigkey(soaquery, NULL);
2110135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2111135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2112135446Strhodes		isc_event_free(&event);
2113135446Strhodes		setzoneclass(dns_rdataclass_none);
2114135446Strhodes		return;
2115135446Strhodes	}
2116170222Sdougb
2117135446Strhodes	isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2118170222Sdougb	reqinfo = NULL;
2119135446Strhodes	isc_event_free(&event);
2120135446Strhodes	reqev = NULL;
2121135446Strhodes
2122135446Strhodes	ddebug("About to create rcvmsg");
2123135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2124135446Strhodes	check_result(result, "dns_message_create");
2125135446Strhodes	result = dns_request_getresponse(request, rcvmsg,
2126135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
2127135446Strhodes	if (result == DNS_R_TSIGERRORSET && userserver != NULL) {
2128135446Strhodes		dns_message_destroy(&rcvmsg);
2129135446Strhodes		ddebug("Destroying request [%p]", request);
2130135446Strhodes		dns_request_destroy(&request);
2131135446Strhodes		reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2132135446Strhodes		if (reqinfo == NULL)
2133135446Strhodes			fatal("out of memory");
2134135446Strhodes		reqinfo->msg = soaquery;
2135135446Strhodes		reqinfo->addr = addr;
2136135446Strhodes		dns_message_renderreset(soaquery);
2137135446Strhodes		ddebug("retrying soa request without TSIG");
2138135446Strhodes		result = dns_request_createvia3(requestmgr, soaquery,
2139135446Strhodes						localaddr, addr, 0, NULL,
2140135446Strhodes						FIND_TIMEOUT * 20,
2141165071Sdougb						FIND_TIMEOUT, 3,
2142135446Strhodes						global_task, recvsoa, reqinfo,
2143135446Strhodes						&request);
2144135446Strhodes		check_result(result, "dns_request_createvia");
2145135446Strhodes		requests++;
2146135446Strhodes		return;
2147135446Strhodes	}
2148135446Strhodes	check_result(result, "dns_request_getresponse");
2149135446Strhodes	section = DNS_SECTION_ANSWER;
2150193149Sdougb	if (debugging)
2151193149Sdougb		show_message(stderr, rcvmsg, "Reply from SOA query:");
2152135446Strhodes
2153135446Strhodes	if (rcvmsg->rcode != dns_rcode_noerror &&
2154135446Strhodes	    rcvmsg->rcode != dns_rcode_nxdomain)
2155135446Strhodes		fatal("response to SOA query was unsuccessful");
2156135446Strhodes
2157170222Sdougb	if (userzone != NULL && rcvmsg->rcode == dns_rcode_nxdomain) {
2158170222Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
2159170222Sdougb		dns_name_format(userzone, namebuf, sizeof(namebuf));
2160170222Sdougb		error("specified zone '%s' does not exist (NXDOMAIN)",
2161170222Sdougb		      namebuf);
2162170222Sdougb		dns_message_destroy(&rcvmsg);
2163170222Sdougb		dns_request_destroy(&request);
2164170222Sdougb		dns_message_destroy(&soaquery);
2165170222Sdougb		ddebug("Out of recvsoa");
2166170222Sdougb		done_update();
2167170222Sdougb		return;
2168170222Sdougb	}
2169170222Sdougb
2170135446Strhodes lookforsoa:
2171135446Strhodes	if (pass == 0)
2172135446Strhodes		section = DNS_SECTION_ANSWER;
2173135446Strhodes	else if (pass == 1)
2174135446Strhodes		section = DNS_SECTION_AUTHORITY;
2175186462Sdougb	else
2176143731Sdougb		goto droplabel;
2177135446Strhodes
2178135446Strhodes	result = dns_message_firstname(rcvmsg, section);
2179135446Strhodes	if (result != ISC_R_SUCCESS) {
2180135446Strhodes		pass++;
2181135446Strhodes		goto lookforsoa;
2182135446Strhodes	}
2183135446Strhodes	while (result == ISC_R_SUCCESS) {
2184135446Strhodes		name = NULL;
2185135446Strhodes		dns_message_currentname(rcvmsg, section, &name);
2186135446Strhodes		soaset = NULL;
2187135446Strhodes		result = dns_message_findtype(name, dns_rdatatype_soa, 0,
2188135446Strhodes					      &soaset);
2189135446Strhodes		if (result == ISC_R_SUCCESS)
2190135446Strhodes			break;
2191135446Strhodes		if (section == DNS_SECTION_ANSWER) {
2192135446Strhodes			dns_rdataset_t *tset = NULL;
2193135446Strhodes			if (dns_message_findtype(name, dns_rdatatype_cname, 0,
2194193149Sdougb						 &tset) == ISC_R_SUCCESS ||
2195135446Strhodes			    dns_message_findtype(name, dns_rdatatype_dname, 0,
2196193149Sdougb						 &tset) == ISC_R_SUCCESS ) {
2197135446Strhodes				seencname = ISC_TRUE;
2198135446Strhodes				break;
2199135446Strhodes			}
2200135446Strhodes		}
2201186462Sdougb
2202135446Strhodes		result = dns_message_nextname(rcvmsg, section);
2203135446Strhodes	}
2204135446Strhodes
2205135446Strhodes	if (soaset == NULL && !seencname) {
2206135446Strhodes		pass++;
2207135446Strhodes		goto lookforsoa;
2208135446Strhodes	}
2209135446Strhodes
2210143731Sdougb	if (seencname)
2211143731Sdougb		goto droplabel;
2212135446Strhodes
2213135446Strhodes	if (debugging) {
2214135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2215135446Strhodes		dns_name_format(name, namestr, sizeof(namestr));
2216135446Strhodes		fprintf(stderr, "Found zone name: %s\n", namestr);
2217135446Strhodes	}
2218135446Strhodes
2219135446Strhodes	result = dns_rdataset_first(soaset);
2220135446Strhodes	check_result(result, "dns_rdataset_first");
2221135446Strhodes
2222135446Strhodes	dns_rdata_init(&soarr);
2223135446Strhodes	dns_rdataset_current(soaset, &soarr);
2224135446Strhodes	result = dns_rdata_tostruct(&soarr, &soa, NULL);
2225135446Strhodes	check_result(result, "dns_rdata_tostruct");
2226135446Strhodes
2227135446Strhodes	dns_name_init(&master, NULL);
2228135446Strhodes	dns_name_clone(&soa.origin, &master);
2229135446Strhodes
2230135446Strhodes	if (userzone != NULL)
2231135446Strhodes		zonename = userzone;
2232135446Strhodes	else
2233135446Strhodes		zonename = name;
2234135446Strhodes
2235135446Strhodes	if (debugging) {
2236135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2237135446Strhodes		dns_name_format(&master, namestr, sizeof(namestr));
2238135446Strhodes		fprintf(stderr, "The master is: %s\n", namestr);
2239135446Strhodes	}
2240135446Strhodes
2241135446Strhodes	if (userserver != NULL)
2242135446Strhodes		serveraddr = userserver;
2243135446Strhodes	else {
2244135446Strhodes		char serverstr[DNS_NAME_MAXTEXT+1];
2245135446Strhodes		isc_buffer_t buf;
2246135446Strhodes
2247135446Strhodes		isc_buffer_init(&buf, serverstr, sizeof(serverstr));
2248135446Strhodes		result = dns_name_totext(&master, ISC_TRUE, &buf);
2249135446Strhodes		check_result(result, "dns_name_totext");
2250135446Strhodes		serverstr[isc_buffer_usedlength(&buf)] = 0;
2251135446Strhodes		get_address(serverstr, DNSDEFAULTPORT, &tempaddr);
2252135446Strhodes		serveraddr = &tempaddr;
2253135446Strhodes	}
2254143731Sdougb	dns_rdata_freestruct(&soa);
2255135446Strhodes
2256193149Sdougb#ifdef GSSAPI
2257193149Sdougb	if (usegsstsig) {
2258193149Sdougb		dns_name_init(&tmpzonename, NULL);
2259193149Sdougb		dns_name_dup(zonename, mctx, &tmpzonename);
2260193149Sdougb		dns_name_init(&restart_master, NULL);
2261193149Sdougb		dns_name_dup(&master, mctx, &restart_master);
2262193149Sdougb		start_gssrequest(&master);
2263193149Sdougb	} else {
2264193149Sdougb		send_update(zonename, serveraddr, localaddr);
2265193149Sdougb		setzoneclass(dns_rdataclass_none);
2266193149Sdougb	}
2267193149Sdougb#else
2268135446Strhodes	send_update(zonename, serveraddr, localaddr);
2269143731Sdougb	setzoneclass(dns_rdataclass_none);
2270193149Sdougb#endif
2271135446Strhodes
2272135446Strhodes	dns_message_destroy(&soaquery);
2273135446Strhodes	dns_request_destroy(&request);
2274135446Strhodes
2275135446Strhodes out:
2276135446Strhodes	dns_message_destroy(&rcvmsg);
2277135446Strhodes	ddebug("Out of recvsoa");
2278143731Sdougb	return;
2279186462Sdougb
2280143731Sdougb droplabel:
2281143731Sdougb	result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
2282143731Sdougb	INSIST(result == ISC_R_SUCCESS);
2283143731Sdougb	name = NULL;
2284143731Sdougb	dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
2285143731Sdougb	nlabels = dns_name_countlabels(name);
2286143731Sdougb	if (nlabels == 1)
2287143731Sdougb		fatal("could not find enclosing zone");
2288143731Sdougb	dns_name_init(&tname, NULL);
2289143731Sdougb	dns_name_getlabelsequence(name, 1, nlabels - 1, &tname);
2290143731Sdougb	dns_name_clone(&tname, name);
2291143731Sdougb	dns_request_destroy(&request);
2292143731Sdougb	dns_message_renderreset(soaquery);
2293153816Sdougb	dns_message_settsigkey(soaquery, NULL);
2294143731Sdougb	if (userserver != NULL)
2295143731Sdougb		sendrequest(localaddr, userserver, soaquery, &request);
2296143731Sdougb	else
2297193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2298143731Sdougb	goto out;
2299135446Strhodes}
2300135446Strhodes
2301135446Strhodesstatic void
2302135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2303135446Strhodes	    dns_message_t *msg, dns_request_t **request)
2304135446Strhodes{
2305135446Strhodes	isc_result_t result;
2306135446Strhodes	nsu_requestinfo_t *reqinfo;
2307135446Strhodes
2308135446Strhodes	reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2309135446Strhodes	if (reqinfo == NULL)
2310135446Strhodes		fatal("out of memory");
2311135446Strhodes	reqinfo->msg = msg;
2312135446Strhodes	reqinfo->addr = destaddr;
2313135446Strhodes	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr, 0,
2314135446Strhodes					(userserver != NULL) ? tsigkey : NULL,
2315135446Strhodes					FIND_TIMEOUT * 20, FIND_TIMEOUT, 3,
2316135446Strhodes					global_task, recvsoa, reqinfo, request);
2317135446Strhodes	check_result(result, "dns_request_createvia");
2318135446Strhodes	requests++;
2319135446Strhodes}
2320135446Strhodes
2321193149Sdougb#ifdef GSSAPI
2322135446Strhodesstatic void
2323193149Sdougbstart_gssrequest(dns_name_t *master)
2324193149Sdougb{
2325193149Sdougb	gss_ctx_id_t context;
2326193149Sdougb	isc_buffer_t buf;
2327193149Sdougb	isc_result_t result;
2328193149Sdougb	isc_uint32_t val = 0;
2329193149Sdougb	dns_message_t *rmsg;
2330193149Sdougb	dns_request_t *request = NULL;
2331193149Sdougb	dns_name_t *servname;
2332193149Sdougb	dns_fixedname_t fname;
2333193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
2334193149Sdougb	char keystr[DNS_NAME_FORMATSIZE];
2335193149Sdougb
2336193149Sdougb	debug("start_gssrequest");
2337193149Sdougb	usevc = ISC_TRUE;
2338193149Sdougb
2339193149Sdougb	if (gssring != NULL)
2340193149Sdougb		dns_tsigkeyring_destroy(&gssring);
2341193149Sdougb	gssring = NULL;
2342193149Sdougb	result = dns_tsigkeyring_create(mctx, &gssring);
2343193149Sdougb
2344193149Sdougb	if (result != ISC_R_SUCCESS)
2345193149Sdougb		fatal("dns_tsigkeyring_create failed: %s",
2346193149Sdougb		      isc_result_totext(result));
2347193149Sdougb
2348193149Sdougb	dns_name_format(master, namestr, sizeof(namestr));
2349193149Sdougb	if (kserver == NULL) {
2350193149Sdougb		kserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
2351193149Sdougb		if (kserver == NULL)
2352193149Sdougb			fatal("out of memory");
2353193149Sdougb	}
2354193149Sdougb	if (userserver == NULL)
2355193149Sdougb		get_address(namestr, DNSDEFAULTPORT, kserver);
2356193149Sdougb	else
2357193149Sdougb		(void)memcpy(kserver, userserver, sizeof(isc_sockaddr_t));
2358193149Sdougb
2359193149Sdougb	dns_fixedname_init(&fname);
2360193149Sdougb	servname = dns_fixedname_name(&fname);
2361193149Sdougb
2362193149Sdougb	result = isc_string_printf(servicename, sizeof(servicename),
2363218384Sdougb				   "DNS/%s%s", namestr, realm ? realm : "");
2364193149Sdougb	if (result != ISC_R_SUCCESS)
2365193149Sdougb		fatal("isc_string_printf(servicename) failed: %s",
2366193149Sdougb		      isc_result_totext(result));
2367193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2368193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2369193149Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname,
2370193149Sdougb				   ISC_FALSE, NULL);
2371193149Sdougb	if (result != ISC_R_SUCCESS)
2372193149Sdougb		fatal("dns_name_fromtext(servname) failed: %s",
2373193149Sdougb		      isc_result_totext(result));
2374193149Sdougb
2375193149Sdougb	dns_fixedname_init(&fkname);
2376193149Sdougb	keyname = dns_fixedname_name(&fkname);
2377193149Sdougb
2378193149Sdougb	isc_random_get(&val);
2379193149Sdougb	result = isc_string_printf(keystr, sizeof(keystr), "%u.sig-%s",
2380193149Sdougb				   val, namestr);
2381193149Sdougb	if (result != ISC_R_SUCCESS)
2382193149Sdougb		fatal("isc_string_printf(keystr) failed: %s",
2383193149Sdougb		      isc_result_totext(result));
2384193149Sdougb	isc_buffer_init(&buf, keystr, strlen(keystr));
2385193149Sdougb	isc_buffer_add(&buf, strlen(keystr));
2386193149Sdougb
2387193149Sdougb	result = dns_name_fromtext(keyname, &buf, dns_rootname,
2388193149Sdougb				   ISC_FALSE, NULL);
2389193149Sdougb	if (result != ISC_R_SUCCESS)
2390193149Sdougb		fatal("dns_name_fromtext(keyname) failed: %s",
2391193149Sdougb		      isc_result_totext(result));
2392193149Sdougb
2393193149Sdougb	/* Windows doesn't recognize name compression in the key name. */
2394193149Sdougb	keyname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2395193149Sdougb
2396193149Sdougb	rmsg = NULL;
2397193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &rmsg);
2398193149Sdougb	if (result != ISC_R_SUCCESS)
2399193149Sdougb		fatal("dns_message_create failed: %s",
2400193149Sdougb		      isc_result_totext(result));
2401193149Sdougb
2402193149Sdougb	/* Build first request. */
2403193149Sdougb	context = GSS_C_NO_CONTEXT;
2404193149Sdougb	result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0,
2405193149Sdougb					&context, use_win2k_gsstsig);
2406193149Sdougb	if (result == ISC_R_FAILURE)
2407193149Sdougb		fatal("Check your Kerberos ticket, it may have expired.");
2408193149Sdougb	if (result != ISC_R_SUCCESS)
2409193149Sdougb		fatal("dns_tkey_buildgssquery failed: %s",
2410193149Sdougb		      isc_result_totext(result));
2411193149Sdougb
2412193149Sdougb	send_gssrequest(localaddr, kserver, rmsg, &request, context);
2413193149Sdougb}
2414193149Sdougb
2415193149Sdougbstatic void
2416193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2417193149Sdougb		dns_message_t *msg, dns_request_t **request,
2418193149Sdougb		gss_ctx_id_t context)
2419193149Sdougb{
2420193149Sdougb	isc_result_t result;
2421193149Sdougb	nsu_gssinfo_t *reqinfo;
2422193149Sdougb	unsigned int options = 0;
2423193149Sdougb
2424193149Sdougb	debug("send_gssrequest");
2425193149Sdougb	reqinfo = isc_mem_get(mctx, sizeof(nsu_gssinfo_t));
2426193149Sdougb	if (reqinfo == NULL)
2427193149Sdougb		fatal("out of memory");
2428193149Sdougb	reqinfo->msg = msg;
2429193149Sdougb	reqinfo->addr = destaddr;
2430193149Sdougb	reqinfo->context = context;
2431193149Sdougb
2432193149Sdougb	options |= DNS_REQUESTOPT_TCP;
2433193149Sdougb	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr,
2434193149Sdougb					options, tsigkey, FIND_TIMEOUT * 20,
2435193149Sdougb					FIND_TIMEOUT, 3, global_task, recvgss,
2436193149Sdougb					reqinfo, request);
2437193149Sdougb	check_result(result, "dns_request_createvia3");
2438193149Sdougb	if (debugging)
2439193149Sdougb		show_message(stdout, msg, "Outgoing update query:");
2440193149Sdougb	requests++;
2441193149Sdougb}
2442193149Sdougb
2443193149Sdougbstatic void
2444193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event) {
2445193149Sdougb	dns_requestevent_t *reqev = NULL;
2446193149Sdougb	dns_request_t *request = NULL;
2447193149Sdougb	isc_result_t result, eresult;
2448193149Sdougb	dns_message_t *rcvmsg = NULL;
2449193149Sdougb	nsu_gssinfo_t *reqinfo;
2450193149Sdougb	dns_message_t *tsigquery = NULL;
2451193149Sdougb	isc_sockaddr_t *addr;
2452193149Sdougb	gss_ctx_id_t context;
2453193149Sdougb	isc_buffer_t buf;
2454193149Sdougb	dns_name_t *servname;
2455193149Sdougb	dns_fixedname_t fname;
2456193149Sdougb
2457193149Sdougb	UNUSED(task);
2458193149Sdougb
2459193149Sdougb	ddebug("recvgss()");
2460193149Sdougb
2461193149Sdougb	requests--;
2462193149Sdougb
2463193149Sdougb	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2464193149Sdougb	reqev = (dns_requestevent_t *)event;
2465193149Sdougb	request = reqev->request;
2466193149Sdougb	eresult = reqev->result;
2467193149Sdougb	reqinfo = reqev->ev_arg;
2468193149Sdougb	tsigquery = reqinfo->msg;
2469193149Sdougb	context = reqinfo->context;
2470193149Sdougb	addr = reqinfo->addr;
2471193149Sdougb
2472193149Sdougb	if (shuttingdown) {
2473193149Sdougb		dns_request_destroy(&request);
2474193149Sdougb		dns_message_destroy(&tsigquery);
2475193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2476193149Sdougb		isc_event_free(&event);
2477193149Sdougb		maybeshutdown();
2478193149Sdougb		return;
2479193149Sdougb	}
2480193149Sdougb
2481193149Sdougb	if (eresult != ISC_R_SUCCESS) {
2482193149Sdougb		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2483193149Sdougb
2484193149Sdougb		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2485193149Sdougb		fprintf(stderr, "; Communication with %s failed: %s\n",
2486193149Sdougb			addrbuf, isc_result_totext(eresult));
2487193149Sdougb		if (userserver != NULL)
2488193149Sdougb			fatal("could not talk to specified name server");
2489193149Sdougb		else if (++ns_inuse >= lwconf->nsnext)
2490193149Sdougb			fatal("could not talk to any default name server");
2491193149Sdougb		ddebug("Destroying request [%p]", request);
2492193149Sdougb		dns_request_destroy(&request);
2493193149Sdougb		dns_message_renderreset(tsigquery);
2494193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], tsigquery,
2495193149Sdougb			    &request);
2496193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2497193149Sdougb		isc_event_free(&event);
2498193149Sdougb		return;
2499193149Sdougb	}
2500193149Sdougb	isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2501193149Sdougb
2502193149Sdougb	isc_event_free(&event);
2503193149Sdougb	reqev = NULL;
2504193149Sdougb
2505193149Sdougb	ddebug("recvgss creating rcvmsg");
2506193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2507193149Sdougb	check_result(result, "dns_message_create");
2508193149Sdougb
2509193149Sdougb	result = dns_request_getresponse(request, rcvmsg,
2510193149Sdougb					 DNS_MESSAGEPARSE_PRESERVEORDER);
2511193149Sdougb	check_result(result, "dns_request_getresponse");
2512193149Sdougb
2513193149Sdougb	if (debugging)
2514193149Sdougb		show_message(stderr, rcvmsg,
2515193149Sdougb			     "recvmsg reply from GSS-TSIG query");
2516193149Sdougb
2517193149Sdougb	if (rcvmsg->rcode == dns_rcode_formerr && !tried_other_gsstsig) {
2518193149Sdougb		ddebug("recvgss trying %s GSS-TSIG",
2519193149Sdougb		       use_win2k_gsstsig ? "Standard" : "Win2k");
2520193149Sdougb		if (use_win2k_gsstsig)
2521193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
2522193149Sdougb		else
2523193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
2524193149Sdougb		tried_other_gsstsig = ISC_TRUE;
2525193149Sdougb		start_gssrequest(&restart_master);
2526193149Sdougb		goto done;
2527193149Sdougb	}
2528193149Sdougb
2529193149Sdougb	if (rcvmsg->rcode != dns_rcode_noerror &&
2530193149Sdougb	    rcvmsg->rcode != dns_rcode_nxdomain)
2531193149Sdougb		fatal("response to GSS-TSIG query was unsuccessful");
2532193149Sdougb
2533193149Sdougb
2534193149Sdougb	dns_fixedname_init(&fname);
2535193149Sdougb	servname = dns_fixedname_name(&fname);
2536193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2537193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2538193149Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname,
2539193149Sdougb				   ISC_FALSE, NULL);
2540193149Sdougb	check_result(result, "dns_name_fromtext");
2541193149Sdougb
2542193149Sdougb	tsigkey = NULL;
2543193149Sdougb	result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname,
2544193149Sdougb				       &context, &tsigkey, gssring,
2545193149Sdougb				       use_win2k_gsstsig);
2546193149Sdougb	switch (result) {
2547193149Sdougb
2548193149Sdougb	case DNS_R_CONTINUE:
2549193149Sdougb		send_gssrequest(localaddr, kserver, tsigquery, &request,
2550193149Sdougb				context);
2551193149Sdougb		break;
2552193149Sdougb
2553193149Sdougb	case ISC_R_SUCCESS:
2554193149Sdougb		/*
2555193149Sdougb		 * XXXSRA Waaay too much fun here.  There's no good
2556193149Sdougb		 * reason why we need a TSIG here (the people who put
2557193149Sdougb		 * it into the spec admitted at the time that it was
2558193149Sdougb		 * not a security issue), and Windows clients don't
2559193149Sdougb		 * seem to work if named complies with the spec and
2560193149Sdougb		 * includes the gratuitous TSIG.  So we're in the
2561193149Sdougb		 * bizarre situation of having to choose between
2562193149Sdougb		 * complying with a useless requirement in the spec
2563193149Sdougb		 * and interoperating.  This is nuts.  If we can
2564193149Sdougb		 * confirm this behavior, we should ask the WG to
2565193149Sdougb		 * consider removing the requirement for the
2566193149Sdougb		 * gratuitous TSIG here.  For the moment, we ignore
2567193149Sdougb		 * the TSIG -- this too is a spec violation, but it's
2568193149Sdougb		 * the least insane thing to do.
2569193149Sdougb		 */
2570193149Sdougb#if 0
2571193149Sdougb		/*
2572193149Sdougb		 * Verify the signature.
2573193149Sdougb		 */
2574193149Sdougb		rcvmsg->state = DNS_SECTION_ANY;
2575193149Sdougb		dns_message_setquerytsig(rcvmsg, NULL);
2576193149Sdougb		result = dns_message_settsigkey(rcvmsg, tsigkey);
2577193149Sdougb		check_result(result, "dns_message_settsigkey");
2578193149Sdougb		result = dns_message_checksig(rcvmsg, NULL);
2579193149Sdougb		ddebug("tsig verification: %s", dns_result_totext(result));
2580193149Sdougb		check_result(result, "dns_message_checksig");
2581193149Sdougb#endif /* 0 */
2582193149Sdougb
2583193149Sdougb		send_update(&tmpzonename, serveraddr, localaddr);
2584193149Sdougb		setzoneclass(dns_rdataclass_none);
2585193149Sdougb		break;
2586193149Sdougb
2587193149Sdougb	default:
2588193149Sdougb		fatal("dns_tkey_negotiategss: %s", isc_result_totext(result));
2589193149Sdougb	}
2590193149Sdougb
2591193149Sdougb done:
2592193149Sdougb	dns_request_destroy(&request);
2593193149Sdougb	dns_message_destroy(&tsigquery);
2594193149Sdougb
2595193149Sdougb	dns_message_destroy(&rcvmsg);
2596193149Sdougb	ddebug("Out of recvgss");
2597193149Sdougb}
2598193149Sdougb#endif
2599193149Sdougb
2600193149Sdougbstatic void
2601135446Strhodesstart_update(void) {
2602135446Strhodes	isc_result_t result;
2603135446Strhodes	dns_rdataset_t *rdataset = NULL;
2604135446Strhodes	dns_name_t *name = NULL;
2605135446Strhodes	dns_request_t *request = NULL;
2606135446Strhodes	dns_message_t *soaquery = NULL;
2607135446Strhodes	dns_name_t *firstname;
2608135446Strhodes	dns_section_t section = DNS_SECTION_UPDATE;
2609135446Strhodes
2610135446Strhodes	ddebug("start_update()");
2611135446Strhodes
2612135446Strhodes	if (answer != NULL)
2613135446Strhodes		dns_message_destroy(&answer);
2614135446Strhodes
2615193149Sdougb	if (userzone != NULL && userserver != NULL && ! usegsstsig) {
2616135446Strhodes		send_update(userzone, userserver, localaddr);
2617135446Strhodes		setzoneclass(dns_rdataclass_none);
2618135446Strhodes		return;
2619135446Strhodes	}
2620135446Strhodes
2621135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
2622135446Strhodes				    &soaquery);
2623135446Strhodes	check_result(result, "dns_message_create");
2624135446Strhodes
2625170222Sdougb	if (userserver == NULL)
2626170222Sdougb		soaquery->flags |= DNS_MESSAGEFLAG_RD;
2627135446Strhodes
2628135446Strhodes	result = dns_message_gettempname(soaquery, &name);
2629135446Strhodes	check_result(result, "dns_message_gettempname");
2630135446Strhodes
2631135446Strhodes	result = dns_message_gettemprdataset(soaquery, &rdataset);
2632135446Strhodes	check_result(result, "dns_message_gettemprdataset");
2633135446Strhodes
2634135446Strhodes	dns_rdataset_makequestion(rdataset, getzoneclass(), dns_rdatatype_soa);
2635135446Strhodes
2636170222Sdougb	if (userzone != NULL) {
2637170222Sdougb		dns_name_init(name, NULL);
2638170222Sdougb		dns_name_clone(userzone, name);
2639170222Sdougb	} else {
2640218384Sdougb		dns_rdataset_t *tmprdataset;
2641170222Sdougb		result = dns_message_firstname(updatemsg, section);
2642170222Sdougb		if (result == ISC_R_NOMORE) {
2643170222Sdougb			section = DNS_SECTION_PREREQUISITE;
2644170222Sdougb			result = dns_message_firstname(updatemsg, section);
2645170222Sdougb		}
2646170222Sdougb		if (result != ISC_R_SUCCESS) {
2647174187Sdougb			dns_message_puttempname(soaquery, &name);
2648174187Sdougb			dns_rdataset_disassociate(rdataset);
2649174187Sdougb			dns_message_puttemprdataset(soaquery, &rdataset);
2650174187Sdougb			dns_message_destroy(&soaquery);
2651170222Sdougb			done_update();
2652170222Sdougb			return;
2653170222Sdougb		}
2654170222Sdougb		firstname = NULL;
2655170222Sdougb		dns_message_currentname(updatemsg, section, &firstname);
2656170222Sdougb		dns_name_init(name, NULL);
2657170222Sdougb		dns_name_clone(firstname, name);
2658218384Sdougb		/*
2659218384Sdougb		 * Looks to see if the first name references a DS record
2660218384Sdougb		 * and if that name is not the root remove a label as DS
2661218384Sdougb		 * records live in the parent zone so we need to start our
2662218384Sdougb		 * search one label up.
2663218384Sdougb		 */
2664218384Sdougb		tmprdataset = ISC_LIST_HEAD(firstname->list);
2665218384Sdougb		if (section == DNS_SECTION_UPDATE &&
2666218384Sdougb		    !dns_name_equal(firstname, dns_rootname) &&
2667218384Sdougb		    tmprdataset->type == dns_rdatatype_ds) {
2668218384Sdougb		    unsigned int labels = dns_name_countlabels(name);
2669218384Sdougb		    dns_name_getlabelsequence(name, 1, labels - 1, name);
2670218384Sdougb		}
2671170222Sdougb	}
2672135446Strhodes
2673135446Strhodes	ISC_LIST_INIT(name->list);
2674135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
2675135446Strhodes	dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
2676135446Strhodes
2677135446Strhodes	if (userserver != NULL)
2678135446Strhodes		sendrequest(localaddr, userserver, soaquery, &request);
2679135446Strhodes	else {
2680135446Strhodes		ns_inuse = 0;
2681135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2682135446Strhodes	}
2683135446Strhodes}
2684135446Strhodes
2685135446Strhodesstatic void
2686135446Strhodescleanup(void) {
2687135446Strhodes	ddebug("cleanup()");
2688135446Strhodes
2689135446Strhodes	if (answer != NULL)
2690135446Strhodes		dns_message_destroy(&answer);
2691193149Sdougb
2692193149Sdougb#ifdef GSSAPI
2693193149Sdougb	if (tsigkey != NULL) {
2694193149Sdougb		ddebug("detach tsigkey x%p", tsigkey);
2695193149Sdougb		dns_tsigkey_detach(&tsigkey);
2696193149Sdougb	}
2697193149Sdougb	if (gssring != NULL) {
2698193149Sdougb		ddebug("Destroying GSS-TSIG keyring");
2699193149Sdougb		dns_tsigkeyring_destroy(&gssring);
2700193149Sdougb	}
2701193149Sdougb	if (kserver != NULL) {
2702193149Sdougb		isc_mem_put(mctx, kserver, sizeof(isc_sockaddr_t));
2703193149Sdougb		kserver = NULL;
2704193149Sdougb	}
2705218384Sdougb	if (realm != NULL) {
2706218384Sdougb		isc_mem_free(mctx, realm);
2707218384Sdougb		realm = NULL;
2708218384Sdougb	}
2709193149Sdougb#endif
2710193149Sdougb
2711135446Strhodes	ddebug("Shutting down task manager");
2712135446Strhodes	isc_taskmgr_destroy(&taskmgr);
2713135446Strhodes
2714135446Strhodes	ddebug("Destroying event");
2715135446Strhodes	isc_event_free(&global_event);
2716135446Strhodes
2717135446Strhodes	ddebug("Shutting down socket manager");
2718135446Strhodes	isc_socketmgr_destroy(&socketmgr);
2719135446Strhodes
2720135446Strhodes	ddebug("Shutting down timer manager");
2721135446Strhodes	isc_timermgr_destroy(&timermgr);
2722135446Strhodes
2723135446Strhodes	ddebug("Destroying hash context");
2724135446Strhodes	isc_hash_destroy();
2725135446Strhodes
2726170222Sdougb	ddebug("Destroying name state");
2727170222Sdougb	dns_name_destroy();
2728170222Sdougb
2729193149Sdougb	ddebug("Removing log context");
2730193149Sdougb	isc_log_destroy(&lctx);
2731193149Sdougb
2732135446Strhodes	ddebug("Destroying memory context");
2733135446Strhodes	if (memdebugging)
2734135446Strhodes		isc_mem_stats(mctx, stderr);
2735135446Strhodes	isc_mem_destroy(&mctx);
2736135446Strhodes}
2737135446Strhodes
2738135446Strhodesstatic void
2739135446Strhodesgetinput(isc_task_t *task, isc_event_t *event) {
2740135446Strhodes	isc_boolean_t more;
2741135446Strhodes
2742135446Strhodes	UNUSED(task);
2743135446Strhodes
2744135446Strhodes	if (shuttingdown) {
2745135446Strhodes		maybeshutdown();
2746135446Strhodes		return;
2747135446Strhodes	}
2748135446Strhodes
2749135446Strhodes	if (global_event == NULL)
2750135446Strhodes		global_event = event;
2751135446Strhodes
2752135446Strhodes	reset_system();
2753135446Strhodes	more = user_interaction();
2754135446Strhodes	if (!more) {
2755135446Strhodes		isc_app_shutdown();
2756135446Strhodes		return;
2757135446Strhodes	}
2758135446Strhodes	start_update();
2759135446Strhodes	return;
2760135446Strhodes}
2761135446Strhodes
2762135446Strhodesint
2763135446Strhodesmain(int argc, char **argv) {
2764135446Strhodes	isc_result_t result;
2765135446Strhodes	style = &dns_master_style_debug;
2766135446Strhodes
2767135446Strhodes	input = stdin;
2768135446Strhodes
2769135446Strhodes	interactive = ISC_TF(isatty(0));
2770135446Strhodes
2771135446Strhodes	isc_app_start();
2772135446Strhodes
2773193149Sdougb	pre_parse_args(argc, argv);
2774135446Strhodes
2775193149Sdougb	result = isc_mem_create(0, 0, &mctx);
2776193149Sdougb	check_result(result, "isc_mem_create");
2777193149Sdougb
2778193149Sdougb	parse_args(argc, argv, mctx, &entropy);
2779193149Sdougb
2780135446Strhodes	setup_system();
2781135446Strhodes
2782135446Strhodes	result = isc_app_onrun(mctx, global_task, getinput, NULL);
2783135446Strhodes	check_result(result, "isc_app_onrun");
2784135446Strhodes
2785135446Strhodes	(void)isc_app_run();
2786135446Strhodes
2787135446Strhodes	cleanup();
2788135446Strhodes
2789135446Strhodes	isc_app_finish();
2790135446Strhodes
2791135446Strhodes	if (seenerror)
2792135446Strhodes		return (2);
2793135446Strhodes	else
2794135446Strhodes		return (0);
2795135446Strhodes}
2796