nsupdate.c revision 225361
1135446Strhodes/*
2224092Sdougb * Copyright (C) 2004-2011  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
18225361Sdougb/* $Id: nsupdate.c,v 1.193.12.3 2011-05-23 22:12:14 each 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>
36224092Sdougb#include <isc/file.h>
37135446Strhodes#include <isc/hash.h>
38135446Strhodes#include <isc/lex.h>
39193149Sdougb#include <isc/log.h>
40135446Strhodes#include <isc/mem.h>
41135446Strhodes#include <isc/parseint.h>
42218384Sdougb#include <isc/print.h>
43193149Sdougb#include <isc/random.h>
44135446Strhodes#include <isc/region.h>
45135446Strhodes#include <isc/sockaddr.h>
46135446Strhodes#include <isc/socket.h>
47135446Strhodes#include <isc/stdio.h>
48135446Strhodes#include <isc/string.h>
49135446Strhodes#include <isc/task.h>
50135446Strhodes#include <isc/timer.h>
51135446Strhodes#include <isc/types.h>
52135446Strhodes#include <isc/util.h>
53135446Strhodes
54224092Sdougb#include <isccfg/namedconf.h>
55224092Sdougb
56135446Strhodes#include <dns/callbacks.h>
57135446Strhodes#include <dns/dispatch.h>
58135446Strhodes#include <dns/dnssec.h>
59135446Strhodes#include <dns/events.h>
60135446Strhodes#include <dns/fixedname.h>
61193149Sdougb#include <dns/log.h>
62135446Strhodes#include <dns/masterdump.h>
63135446Strhodes#include <dns/message.h>
64135446Strhodes#include <dns/name.h>
65135446Strhodes#include <dns/rcode.h>
66135446Strhodes#include <dns/rdata.h>
67135446Strhodes#include <dns/rdataclass.h>
68135446Strhodes#include <dns/rdatalist.h>
69135446Strhodes#include <dns/rdataset.h>
70135446Strhodes#include <dns/rdatastruct.h>
71135446Strhodes#include <dns/rdatatype.h>
72135446Strhodes#include <dns/request.h>
73135446Strhodes#include <dns/result.h>
74193149Sdougb#include <dns/tkey.h>
75135446Strhodes#include <dns/tsig.h>
76135446Strhodes
77135446Strhodes#include <dst/dst.h>
78135446Strhodes
79135446Strhodes#include <lwres/lwres.h>
80135446Strhodes#include <lwres/net.h>
81135446Strhodes
82193149Sdougb#ifdef GSSAPI
83193149Sdougb#include <dst/gssapi.h>
84224092Sdougb#include ISC_PLATFORM_KRB5HEADER
85193149Sdougb#endif
86135446Strhodes#include <bind9/getaddresses.h>
87135446Strhodes
88193149Sdougb
89135446Strhodes#ifdef HAVE_ADDRINFO
90135446Strhodes#ifdef HAVE_GETADDRINFO
91135446Strhodes#ifdef HAVE_GAISTRERROR
92135446Strhodes#define USE_GETADDRINFO
93135446Strhodes#endif
94135446Strhodes#endif
95135446Strhodes#endif
96135446Strhodes
97135446Strhodes#ifndef USE_GETADDRINFO
98135446Strhodes#ifndef ISC_PLATFORM_NONSTDHERRNO
99135446Strhodesextern int h_errno;
100135446Strhodes#endif
101135446Strhodes#endif
102135446Strhodes
103135446Strhodes#define MAXCMD (4 * 1024)
104135446Strhodes#define MAXWIRE (64 * 1024)
105135446Strhodes#define PACKETSIZE ((64 * 1024) - 1)
106135446Strhodes#define INITTEXT (2 * 1024)
107135446Strhodes#define MAXTEXT (128 * 1024)
108135446Strhodes#define FIND_TIMEOUT 5
109135446Strhodes#define TTL_MAX 2147483647U	/* Maximum signed 32 bit integer. */
110135446Strhodes
111135446Strhodes#define DNSDEFAULTPORT 53
112135446Strhodes
113224092Sdougbstatic isc_uint16_t dnsport = DNSDEFAULTPORT;
114224092Sdougb
115135446Strhodes#ifndef RESOLV_CONF
116135446Strhodes#define RESOLV_CONF "/etc/resolv.conf"
117135446Strhodes#endif
118135446Strhodes
119135446Strhodesstatic isc_boolean_t debugging = ISC_FALSE, ddebugging = ISC_FALSE;
120135446Strhodesstatic isc_boolean_t memdebugging = ISC_FALSE;
121135446Strhodesstatic isc_boolean_t have_ipv4 = ISC_FALSE;
122135446Strhodesstatic isc_boolean_t have_ipv6 = ISC_FALSE;
123135446Strhodesstatic isc_boolean_t is_dst_up = ISC_FALSE;
124135446Strhodesstatic isc_boolean_t usevc = ISC_FALSE;
125193149Sdougbstatic isc_boolean_t usegsstsig = ISC_FALSE;
126193149Sdougbstatic isc_boolean_t use_win2k_gsstsig = ISC_FALSE;
127193149Sdougbstatic isc_boolean_t tried_other_gsstsig = ISC_FALSE;
128224092Sdougbstatic isc_boolean_t local_only = ISC_FALSE;
129135446Strhodesstatic isc_taskmgr_t *taskmgr = NULL;
130135446Strhodesstatic isc_task_t *global_task = NULL;
131135446Strhodesstatic isc_event_t *global_event = NULL;
132193149Sdougbstatic isc_log_t *lctx = NULL;
133135446Strhodesstatic isc_mem_t *mctx = NULL;
134135446Strhodesstatic dns_dispatchmgr_t *dispatchmgr = NULL;
135135446Strhodesstatic dns_requestmgr_t *requestmgr = NULL;
136135446Strhodesstatic isc_socketmgr_t *socketmgr = NULL;
137135446Strhodesstatic isc_timermgr_t *timermgr = NULL;
138135446Strhodesstatic dns_dispatch_t *dispatchv4 = NULL;
139135446Strhodesstatic dns_dispatch_t *dispatchv6 = NULL;
140135446Strhodesstatic dns_message_t *updatemsg = NULL;
141135446Strhodesstatic dns_fixedname_t fuserzone;
142135446Strhodesstatic dns_name_t *userzone = NULL;
143193149Sdougbstatic dns_name_t *zonename = NULL;
144193149Sdougbstatic dns_name_t tmpzonename;
145193149Sdougbstatic dns_name_t restart_master;
146193149Sdougbstatic dns_tsig_keyring_t *gssring = NULL;
147135446Strhodesstatic dns_tsigkey_t *tsigkey = NULL;
148225361Sdougbstatic dst_key_t *sig0key = NULL;
149135446Strhodesstatic lwres_context_t *lwctx = NULL;
150135446Strhodesstatic lwres_conf_t *lwconf;
151135446Strhodesstatic isc_sockaddr_t *servers;
152135446Strhodesstatic int ns_inuse = 0;
153135446Strhodesstatic int ns_total = 0;
154135446Strhodesstatic isc_sockaddr_t *userserver = NULL;
155135446Strhodesstatic isc_sockaddr_t *localaddr = NULL;
156193149Sdougbstatic isc_sockaddr_t *serveraddr = NULL;
157193149Sdougbstatic isc_sockaddr_t tempaddr;
158224092Sdougbstatic const char *keyfile = NULL;
159224092Sdougbstatic char *keystr = NULL;
160193149Sdougbstatic isc_entropy_t *entropy = NULL;
161135446Strhodesstatic isc_boolean_t shuttingdown = ISC_FALSE;
162135446Strhodesstatic FILE *input;
163135446Strhodesstatic isc_boolean_t interactive = ISC_TRUE;
164135446Strhodesstatic isc_boolean_t seenerror = ISC_FALSE;
165135446Strhodesstatic const dns_master_style_t *style;
166135446Strhodesstatic int requests = 0;
167193149Sdougbstatic unsigned int logdebuglevel = 0;
168135446Strhodesstatic unsigned int timeout = 300;
169135446Strhodesstatic unsigned int udp_timeout = 3;
170135446Strhodesstatic unsigned int udp_retries = 3;
171135446Strhodesstatic dns_rdataclass_t defaultclass = dns_rdataclass_in;
172135446Strhodesstatic dns_rdataclass_t zoneclass = dns_rdataclass_none;
173135446Strhodesstatic dns_message_t *answer = NULL;
174193149Sdougbstatic isc_uint32_t default_ttl = 0;
175193149Sdougbstatic isc_boolean_t default_ttl_set = ISC_FALSE;
176135446Strhodes
177135446Strhodestypedef struct nsu_requestinfo {
178135446Strhodes	dns_message_t *msg;
179135446Strhodes	isc_sockaddr_t *addr;
180135446Strhodes} nsu_requestinfo_t;
181135446Strhodes
182135446Strhodesstatic void
183135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
184135446Strhodes	    dns_message_t *msg, dns_request_t **request);
185135446Strhodes
186224092SdougbISC_PLATFORM_NORETURN_PRE static void
187224092Sdougbfatal(const char *format, ...)
188224092SdougbISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST;
189224092Sdougb
190135446Strhodesstatic void
191135446Strhodesdebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
192135446Strhodes
193135446Strhodesstatic void
194135446Strhodesddebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
195135446Strhodes
196193149Sdougb#ifdef GSSAPI
197193149Sdougbstatic dns_fixedname_t fkname;
198193149Sdougbstatic isc_sockaddr_t *kserver = NULL;
199218384Sdougbstatic char *realm = NULL;
200193149Sdougbstatic char servicename[DNS_NAME_FORMATSIZE];
201193149Sdougbstatic dns_name_t *keyname;
202193149Sdougbtypedef struct nsu_gssinfo {
203193149Sdougb	dns_message_t *msg;
204193149Sdougb	isc_sockaddr_t *addr;
205193149Sdougb	gss_ctx_id_t context;
206193149Sdougb} nsu_gssinfo_t;
207193149Sdougb
208170222Sdougbstatic void
209193149Sdougbstart_gssrequest(dns_name_t *master);
210193149Sdougbstatic void
211193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
212193149Sdougb		dns_message_t *msg, dns_request_t **request,
213193149Sdougb		gss_ctx_id_t context);
214193149Sdougbstatic void
215193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event);
216193149Sdougb#endif /* GSSAPI */
217193149Sdougb
218193149Sdougbstatic void
219170222Sdougberror(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
220170222Sdougb
221135446Strhodes#define STATUS_MORE	(isc_uint16_t)0
222135446Strhodes#define STATUS_SEND	(isc_uint16_t)1
223135446Strhodes#define STATUS_QUIT	(isc_uint16_t)2
224135446Strhodes#define STATUS_SYNTAX	(isc_uint16_t)3
225135446Strhodes
226193149Sdougbtypedef struct entropysource entropysource_t;
227193149Sdougb
228193149Sdougbstruct entropysource {
229193149Sdougb	isc_entropysource_t *source;
230193149Sdougb	isc_mem_t *mctx;
231193149Sdougb	ISC_LINK(entropysource_t) link;
232193149Sdougb};
233193149Sdougb
234193149Sdougbstatic ISC_LIST(entropysource_t) sources;
235193149Sdougb
236193149Sdougbstatic void
237193149Sdougbsetup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx)
238193149Sdougb{
239193149Sdougb	isc_result_t result;
240193149Sdougb	isc_entropysource_t *source = NULL;
241193149Sdougb	entropysource_t *elt;
242193149Sdougb	int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
243193149Sdougb
244193149Sdougb	REQUIRE(ectx != NULL);
245193149Sdougb
246193149Sdougb	if (*ectx == NULL) {
247193149Sdougb		result = isc_entropy_create(mctx, ectx);
248193149Sdougb		if (result != ISC_R_SUCCESS)
249193149Sdougb			fatal("could not create entropy object");
250193149Sdougb		ISC_LIST_INIT(sources);
251193149Sdougb	}
252193149Sdougb
253193149Sdougb	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
254193149Sdougb		usekeyboard = ISC_ENTROPY_KEYBOARDYES;
255193149Sdougb		randomfile = NULL;
256193149Sdougb	}
257193149Sdougb
258193149Sdougb	result = isc_entropy_usebestsource(*ectx, &source, randomfile,
259193149Sdougb					   usekeyboard);
260193149Sdougb
261193149Sdougb	if (result != ISC_R_SUCCESS)
262193149Sdougb		fatal("could not initialize entropy source: %s",
263193149Sdougb		      isc_result_totext(result));
264193149Sdougb
265193149Sdougb	if (source != NULL) {
266193149Sdougb		elt = isc_mem_get(mctx, sizeof(*elt));
267193149Sdougb		if (elt == NULL)
268193149Sdougb			fatal("out of memory");
269193149Sdougb		elt->source = source;
270193149Sdougb		elt->mctx = mctx;
271193149Sdougb		ISC_LINK_INIT(elt, link);
272193149Sdougb		ISC_LIST_APPEND(sources, elt, link);
273193149Sdougb	}
274193149Sdougb}
275193149Sdougb
276193149Sdougbstatic void
277193149Sdougbcleanup_entropy(isc_entropy_t **ectx) {
278193149Sdougb	entropysource_t *source;
279193149Sdougb	while (!ISC_LIST_EMPTY(sources)) {
280193149Sdougb		source = ISC_LIST_HEAD(sources);
281193149Sdougb		ISC_LIST_UNLINK(sources, source, link);
282193149Sdougb		isc_entropy_destroysource(&source->source);
283193149Sdougb		isc_mem_put(source->mctx, source, sizeof(*source));
284193149Sdougb	}
285193149Sdougb	isc_entropy_detach(ectx);
286193149Sdougb}
287193149Sdougb
288193149Sdougb
289135446Strhodesstatic dns_rdataclass_t
290135446Strhodesgetzoneclass(void) {
291135446Strhodes	if (zoneclass == dns_rdataclass_none)
292135446Strhodes		zoneclass = defaultclass;
293135446Strhodes	return (zoneclass);
294135446Strhodes}
295135446Strhodes
296135446Strhodesstatic isc_boolean_t
297135446Strhodessetzoneclass(dns_rdataclass_t rdclass) {
298135446Strhodes	if (zoneclass == dns_rdataclass_none ||
299135446Strhodes	    rdclass == dns_rdataclass_none)
300135446Strhodes		zoneclass = rdclass;
301135446Strhodes	if (zoneclass != rdclass)
302135446Strhodes		return (ISC_FALSE);
303135446Strhodes	return (ISC_TRUE);
304135446Strhodes}
305135446Strhodes
306135446Strhodesstatic void
307135446Strhodesfatal(const char *format, ...) {
308135446Strhodes	va_list args;
309135446Strhodes
310135446Strhodes	va_start(args, format);
311135446Strhodes	vfprintf(stderr, format, args);
312135446Strhodes	va_end(args);
313135446Strhodes	fprintf(stderr, "\n");
314135446Strhodes	exit(1);
315135446Strhodes}
316135446Strhodes
317135446Strhodesstatic void
318170222Sdougberror(const char *format, ...) {
319170222Sdougb	va_list args;
320170222Sdougb
321170222Sdougb	va_start(args, format);
322170222Sdougb	vfprintf(stderr, format, args);
323170222Sdougb	va_end(args);
324170222Sdougb	fprintf(stderr, "\n");
325170222Sdougb}
326170222Sdougb
327170222Sdougbstatic void
328135446Strhodesdebug(const char *format, ...) {
329135446Strhodes	va_list args;
330135446Strhodes
331135446Strhodes	if (debugging) {
332135446Strhodes		va_start(args, format);
333135446Strhodes		vfprintf(stderr, format, args);
334135446Strhodes		va_end(args);
335135446Strhodes		fprintf(stderr, "\n");
336135446Strhodes	}
337135446Strhodes}
338135446Strhodes
339135446Strhodesstatic void
340135446Strhodesddebug(const char *format, ...) {
341135446Strhodes	va_list args;
342135446Strhodes
343135446Strhodes	if (ddebugging) {
344135446Strhodes		va_start(args, format);
345135446Strhodes		vfprintf(stderr, format, args);
346135446Strhodes		va_end(args);
347135446Strhodes		fprintf(stderr, "\n");
348135446Strhodes	}
349135446Strhodes}
350135446Strhodes
351135446Strhodesstatic inline void
352135446Strhodescheck_result(isc_result_t result, const char *msg) {
353135446Strhodes	if (result != ISC_R_SUCCESS)
354135446Strhodes		fatal("%s: %s", msg, isc_result_totext(result));
355135446Strhodes}
356135446Strhodes
357135446Strhodesstatic void *
358135446Strhodesmem_alloc(void *arg, size_t size) {
359135446Strhodes	return (isc_mem_get(arg, size));
360135446Strhodes}
361135446Strhodes
362135446Strhodesstatic void
363135446Strhodesmem_free(void *arg, void *mem, size_t size) {
364135446Strhodes	isc_mem_put(arg, mem, size);
365135446Strhodes}
366135446Strhodes
367135446Strhodesstatic char *
368135446Strhodesnsu_strsep(char **stringp, const char *delim) {
369135446Strhodes	char *string = *stringp;
370135446Strhodes	char *s;
371135446Strhodes	const char *d;
372135446Strhodes	char sc, dc;
373135446Strhodes
374135446Strhodes	if (string == NULL)
375135446Strhodes		return (NULL);
376135446Strhodes
377135446Strhodes	for (; *string != '\0'; string++) {
378135446Strhodes		sc = *string;
379135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
380135446Strhodes			if (sc == dc)
381135446Strhodes				break;
382135446Strhodes		}
383135446Strhodes		if (dc == 0)
384135446Strhodes			break;
385135446Strhodes	}
386135446Strhodes
387135446Strhodes	for (s = string; *s != '\0'; s++) {
388135446Strhodes		sc = *s;
389135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
390135446Strhodes			if (sc == dc) {
391135446Strhodes				*s++ = '\0';
392135446Strhodes				*stringp = s;
393135446Strhodes				return (string);
394135446Strhodes			}
395135446Strhodes		}
396135446Strhodes	}
397135446Strhodes	*stringp = NULL;
398135446Strhodes	return (string);
399135446Strhodes}
400135446Strhodes
401135446Strhodesstatic void
402135446Strhodesreset_system(void) {
403135446Strhodes	isc_result_t result;
404135446Strhodes
405135446Strhodes	ddebug("reset_system()");
406135446Strhodes	/* If the update message is still around, destroy it */
407135446Strhodes	if (updatemsg != NULL)
408135446Strhodes		dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER);
409135446Strhodes	else {
410135446Strhodes		result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
411135446Strhodes					    &updatemsg);
412135446Strhodes		check_result(result, "dns_message_create");
413135446Strhodes	}
414135446Strhodes	updatemsg->opcode = dns_opcode_update;
415193149Sdougb	if (usegsstsig) {
416193149Sdougb		if (tsigkey != NULL)
417193149Sdougb			dns_tsigkey_detach(&tsigkey);
418193149Sdougb		if (gssring != NULL)
419224092Sdougb			dns_tsigkeyring_detach(&gssring);
420193149Sdougb		tried_other_gsstsig = ISC_FALSE;
421193149Sdougb	}
422135446Strhodes}
423135446Strhodes
424170222Sdougbstatic isc_uint16_t
425170222Sdougbparse_hmac(dns_name_t **hmac, const char *hmacstr, size_t len) {
426170222Sdougb	isc_uint16_t digestbits = 0;
427170222Sdougb	isc_result_t result;
428170222Sdougb	char buf[20];
429170222Sdougb
430170222Sdougb	REQUIRE(hmac != NULL && *hmac == NULL);
431170222Sdougb	REQUIRE(hmacstr != NULL);
432170222Sdougb
433170222Sdougb	if (len >= sizeof(buf))
434170222Sdougb		fatal("unknown key type '%.*s'", (int)(len), hmacstr);
435170222Sdougb
436170222Sdougb	strncpy(buf, hmacstr, len);
437170222Sdougb	buf[len] = 0;
438186462Sdougb
439170222Sdougb	if (strcasecmp(buf, "hmac-md5") == 0) {
440170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
441170222Sdougb	} else if (strncasecmp(buf, "hmac-md5-", 9) == 0) {
442170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
443170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[9], 10);
444170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 128)
445170222Sdougb			fatal("digest-bits out of range [0..128]");
446170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
447170222Sdougb	} else if (strcasecmp(buf, "hmac-sha1") == 0) {
448170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
449170222Sdougb	} else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) {
450170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
451170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[10], 10);
452170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 160)
453170222Sdougb			fatal("digest-bits out of range [0..160]");
454170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
455170222Sdougb	} else if (strcasecmp(buf, "hmac-sha224") == 0) {
456170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
457170222Sdougb	} else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) {
458170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
459170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
460170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 224)
461170222Sdougb			fatal("digest-bits out of range [0..224]");
462170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
463170222Sdougb	} else if (strcasecmp(buf, "hmac-sha256") == 0) {
464170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
465170222Sdougb	} else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) {
466170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
467170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
468170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 256)
469170222Sdougb			fatal("digest-bits out of range [0..256]");
470170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
471170222Sdougb	} else if (strcasecmp(buf, "hmac-sha384") == 0) {
472170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
473170222Sdougb	} else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) {
474170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
475170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
476170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 384)
477170222Sdougb			fatal("digest-bits out of range [0..384]");
478170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
479170222Sdougb	} else if (strcasecmp(buf, "hmac-sha512") == 0) {
480170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
481170222Sdougb	} else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) {
482170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
483170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
484170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 512)
485170222Sdougb			fatal("digest-bits out of range [0..512]");
486170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
487170222Sdougb	} else
488170222Sdougb		fatal("unknown key type '%s'", buf);
489170222Sdougb	return (digestbits);
490170222Sdougb}
491170222Sdougb
492224092Sdougbstatic int
493224092Sdougbbasenamelen(const char *file) {
494224092Sdougb	int len = strlen(file);
495224092Sdougb
496224092Sdougb	if (len > 1 && file[len - 1] == '.')
497224092Sdougb		len -= 1;
498224092Sdougb	else if (len > 8 && strcmp(file + len - 8, ".private") == 0)
499224092Sdougb		len -= 8;
500224092Sdougb	else if (len > 4 && strcmp(file + len - 4, ".key") == 0)
501224092Sdougb		len -= 4;
502224092Sdougb	return (len);
503224092Sdougb}
504224092Sdougb
505135446Strhodesstatic void
506135446Strhodessetup_keystr(void) {
507135446Strhodes	unsigned char *secret = NULL;
508135446Strhodes	int secretlen;
509135446Strhodes	isc_buffer_t secretbuf;
510135446Strhodes	isc_result_t result;
511135446Strhodes	isc_buffer_t keynamesrc;
512135446Strhodes	char *secretstr;
513170222Sdougb	char *s, *n;
514135446Strhodes	dns_fixedname_t fkeyname;
515135446Strhodes	dns_name_t *keyname;
516170222Sdougb	char *name;
517170222Sdougb	dns_name_t *hmacname = NULL;
518170222Sdougb	isc_uint16_t digestbits = 0;
519135446Strhodes
520135446Strhodes	dns_fixedname_init(&fkeyname);
521135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
522135446Strhodes
523135446Strhodes	debug("Creating key...");
524135446Strhodes
525135446Strhodes	s = strchr(keystr, ':');
526170222Sdougb	if (s == NULL || s == keystr || s[1] == 0)
527170222Sdougb		fatal("key option must specify [hmac:]keyname:secret");
528135446Strhodes	secretstr = s + 1;
529170222Sdougb	n = strchr(secretstr, ':');
530170222Sdougb	if (n != NULL) {
531170222Sdougb		if (n == secretstr || n[1] == 0)
532170222Sdougb			fatal("key option must specify [hmac:]keyname:secret");
533170222Sdougb		name = secretstr;
534170222Sdougb		secretstr = n + 1;
535170222Sdougb		digestbits = parse_hmac(&hmacname, keystr, s - keystr);
536170222Sdougb	} else {
537170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
538170222Sdougb		name = keystr;
539170222Sdougb		n = s;
540170222Sdougb	}
541135446Strhodes
542170222Sdougb	isc_buffer_init(&keynamesrc, name, n - name);
543170222Sdougb	isc_buffer_add(&keynamesrc, n - name);
544135446Strhodes
545135446Strhodes	debug("namefromtext");
546224092Sdougb	result = dns_name_fromtext(keyname, &keynamesrc, dns_rootname, 0, NULL);
547135446Strhodes	check_result(result, "dns_name_fromtext");
548135446Strhodes
549135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
550135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
551135446Strhodes	if (secret == NULL)
552135446Strhodes		fatal("out of memory");
553135446Strhodes
554135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
555135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
556135446Strhodes	if (result != ISC_R_SUCCESS) {
557135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
558135446Strhodes			keystr, isc_result_totext(result));
559135446Strhodes		goto failure;
560135446Strhodes	}
561135446Strhodes
562135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
563135446Strhodes
564135446Strhodes	debug("keycreate");
565170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
566218384Sdougb				    ISC_FALSE, NULL, 0, 0, mctx, NULL,
567218384Sdougb				    &tsigkey);
568135446Strhodes	if (result != ISC_R_SUCCESS)
569135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
570135446Strhodes			keystr, dns_result_totext(result));
571170222Sdougb	else
572170222Sdougb		dst_key_setbits(tsigkey->key, digestbits);
573135446Strhodes failure:
574135446Strhodes	if (secret != NULL)
575135446Strhodes		isc_mem_free(mctx, secret);
576135446Strhodes}
577135446Strhodes
578224092Sdougb/*
579224092Sdougb * Get a key from a named.conf format keyfile
580224092Sdougb */
581224092Sdougbstatic isc_result_t
582224092Sdougbread_sessionkey(isc_mem_t *mctx, isc_log_t *lctx) {
583224092Sdougb	cfg_parser_t *pctx = NULL;
584224092Sdougb	cfg_obj_t *sessionkey = NULL;
585224092Sdougb	const cfg_obj_t *key = NULL;
586224092Sdougb	const cfg_obj_t *secretobj = NULL;
587224092Sdougb	const cfg_obj_t *algorithmobj = NULL;
588224092Sdougb	const char *keyname;
589224092Sdougb	const char *secretstr;
590224092Sdougb	const char *algorithm;
591224092Sdougb	isc_result_t result;
592224092Sdougb	int len;
593218384Sdougb
594224092Sdougb	if (! isc_file_exists(keyfile))
595224092Sdougb		return (ISC_R_FILENOTFOUND);
596224092Sdougb
597224092Sdougb	result = cfg_parser_create(mctx, lctx, &pctx);
598224092Sdougb	if (result != ISC_R_SUCCESS)
599224092Sdougb		goto cleanup;
600224092Sdougb
601224092Sdougb	result = cfg_parse_file(pctx, keyfile, &cfg_type_sessionkey,
602224092Sdougb				&sessionkey);
603224092Sdougb	if (result != ISC_R_SUCCESS)
604224092Sdougb		goto cleanup;
605224092Sdougb
606224092Sdougb	result = cfg_map_get(sessionkey, "key", &key);
607224092Sdougb	if (result != ISC_R_SUCCESS)
608224092Sdougb		goto cleanup;
609224092Sdougb
610224092Sdougb	(void) cfg_map_get(key, "secret", &secretobj);
611224092Sdougb	(void) cfg_map_get(key, "algorithm", &algorithmobj);
612224092Sdougb	if (secretobj == NULL || algorithmobj == NULL)
613224092Sdougb		fatal("key must have algorithm and secret");
614224092Sdougb
615224092Sdougb	keyname = cfg_obj_asstring(cfg_map_getname(key));
616224092Sdougb	secretstr = cfg_obj_asstring(secretobj);
617224092Sdougb	algorithm = cfg_obj_asstring(algorithmobj);
618224092Sdougb
619224092Sdougb	len = strlen(algorithm) + strlen(keyname) + strlen(secretstr) + 3;
620224092Sdougb	keystr = isc_mem_allocate(mctx, len);
621224092Sdougb	snprintf(keystr, len, "%s:%s:%s", algorithm, keyname, secretstr);
622224092Sdougb	setup_keystr();
623224092Sdougb
624224092Sdougb cleanup:
625224092Sdougb	if (pctx != NULL) {
626224092Sdougb		if (sessionkey != NULL)
627224092Sdougb			cfg_obj_destroy(pctx, &sessionkey);
628224092Sdougb		cfg_parser_destroy(&pctx);
629224092Sdougb	}
630224092Sdougb
631224092Sdougb	if (keystr != NULL)
632224092Sdougb		isc_mem_free(mctx, keystr);
633224092Sdougb
634224092Sdougb	return (result);
635218384Sdougb}
636218384Sdougb
637135446Strhodesstatic void
638224092Sdougbsetup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) {
639135446Strhodes	dst_key_t *dstkey = NULL;
640135446Strhodes	isc_result_t result;
641170222Sdougb	dns_name_t *hmacname = NULL;
642135446Strhodes
643135446Strhodes	debug("Creating key...");
644135446Strhodes
645218384Sdougb	if (sig0key != NULL)
646218384Sdougb		dst_key_free(&sig0key);
647218384Sdougb
648224092Sdougb	/* Try reading the key from a K* pair */
649224092Sdougb	result = dst_key_fromnamedfile(keyfile, NULL,
650135446Strhodes				       DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
651135446Strhodes				       &dstkey);
652224092Sdougb
653224092Sdougb	/* If that didn't work, try reading it as a session.key keyfile */
654135446Strhodes	if (result != ISC_R_SUCCESS) {
655224092Sdougb		result = read_sessionkey(mctx, lctx);
656224092Sdougb		if (result == ISC_R_SUCCESS)
657224092Sdougb			return;
658224092Sdougb	}
659224092Sdougb
660224092Sdougb	if (result != ISC_R_SUCCESS) {
661218384Sdougb		fprintf(stderr, "could not read key from %.*s.{private,key}: "
662218384Sdougb				"%s\n", basenamelen(keyfile), keyfile,
663218384Sdougb				isc_result_totext(result));
664135446Strhodes		return;
665135446Strhodes	}
666224092Sdougb
667170222Sdougb	switch (dst_key_alg(dstkey)) {
668170222Sdougb	case DST_ALG_HMACMD5:
669170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
670170222Sdougb		break;
671170222Sdougb	case DST_ALG_HMACSHA1:
672170222Sdougb		hmacname = DNS_TSIG_HMACSHA1_NAME;
673170222Sdougb		break;
674170222Sdougb	case DST_ALG_HMACSHA224:
675170222Sdougb		hmacname = DNS_TSIG_HMACSHA224_NAME;
676170222Sdougb		break;
677170222Sdougb	case DST_ALG_HMACSHA256:
678170222Sdougb		hmacname = DNS_TSIG_HMACSHA256_NAME;
679170222Sdougb		break;
680170222Sdougb	case DST_ALG_HMACSHA384:
681170222Sdougb		hmacname = DNS_TSIG_HMACSHA384_NAME;
682170222Sdougb		break;
683170222Sdougb	case DST_ALG_HMACSHA512:
684170222Sdougb		hmacname = DNS_TSIG_HMACSHA512_NAME;
685170222Sdougb		break;
686170222Sdougb	}
687170222Sdougb	if (hmacname != NULL) {
688135446Strhodes		result = dns_tsigkey_createfromkey(dst_key_name(dstkey),
689170222Sdougb						   hmacname, dstkey, ISC_FALSE,
690170222Sdougb						   NULL, 0, 0, mctx, NULL,
691170222Sdougb						   &tsigkey);
692218384Sdougb		dst_key_free(&dstkey);
693135446Strhodes		if (result != ISC_R_SUCCESS) {
694135446Strhodes			fprintf(stderr, "could not create key from %s: %s\n",
695135446Strhodes				keyfile, isc_result_totext(result));
696135446Strhodes			return;
697135446Strhodes		}
698222395Sdougb	} else {
699218384Sdougb		dst_key_attach(dstkey, &sig0key);
700222395Sdougb		dst_key_free(&dstkey);
701222395Sdougb	}
702135446Strhodes}
703135446Strhodes
704135446Strhodesstatic void
705135446Strhodesdoshutdown(void) {
706135446Strhodes	isc_task_detach(&global_task);
707135446Strhodes
708135446Strhodes	if (userserver != NULL)
709135446Strhodes		isc_mem_put(mctx, userserver, sizeof(isc_sockaddr_t));
710135446Strhodes
711135446Strhodes	if (localaddr != NULL)
712135446Strhodes		isc_mem_put(mctx, localaddr, sizeof(isc_sockaddr_t));
713135446Strhodes
714135446Strhodes	if (tsigkey != NULL) {
715135446Strhodes		ddebug("Freeing TSIG key");
716135446Strhodes		dns_tsigkey_detach(&tsigkey);
717135446Strhodes	}
718135446Strhodes
719135446Strhodes	if (sig0key != NULL) {
720135446Strhodes		ddebug("Freeing SIG(0) key");
721135446Strhodes		dst_key_free(&sig0key);
722135446Strhodes	}
723135446Strhodes
724135446Strhodes	if (updatemsg != NULL)
725135446Strhodes		dns_message_destroy(&updatemsg);
726135446Strhodes
727135446Strhodes	if (is_dst_up) {
728135446Strhodes		ddebug("Destroy DST lib");
729135446Strhodes		dst_lib_destroy();
730135446Strhodes		is_dst_up = ISC_FALSE;
731135446Strhodes	}
732135446Strhodes
733193149Sdougb	cleanup_entropy(&entropy);
734135446Strhodes
735135446Strhodes	lwres_conf_clear(lwctx);
736135446Strhodes	lwres_context_destroy(&lwctx);
737135446Strhodes
738135446Strhodes	isc_mem_put(mctx, servers, ns_total * sizeof(isc_sockaddr_t));
739135446Strhodes
740135446Strhodes	ddebug("Destroying request manager");
741135446Strhodes	dns_requestmgr_detach(&requestmgr);
742135446Strhodes
743135446Strhodes	ddebug("Freeing the dispatchers");
744135446Strhodes	if (have_ipv4)
745135446Strhodes		dns_dispatch_detach(&dispatchv4);
746135446Strhodes	if (have_ipv6)
747135446Strhodes		dns_dispatch_detach(&dispatchv6);
748135446Strhodes
749135446Strhodes	ddebug("Shutting down dispatch manager");
750135446Strhodes	dns_dispatchmgr_destroy(&dispatchmgr);
751135446Strhodes
752135446Strhodes}
753135446Strhodes
754135446Strhodesstatic void
755135446Strhodesmaybeshutdown(void) {
756135446Strhodes	ddebug("Shutting down request manager");
757135446Strhodes	dns_requestmgr_shutdown(requestmgr);
758135446Strhodes
759135446Strhodes	if (requests != 0)
760135446Strhodes		return;
761135446Strhodes
762135446Strhodes	doshutdown();
763135446Strhodes}
764135446Strhodes
765135446Strhodesstatic void
766135446Strhodesshutdown_program(isc_task_t *task, isc_event_t *event) {
767135446Strhodes	REQUIRE(task == global_task);
768135446Strhodes	UNUSED(task);
769135446Strhodes
770135446Strhodes	ddebug("shutdown_program()");
771135446Strhodes	isc_event_free(&event);
772135446Strhodes
773135446Strhodes	shuttingdown = ISC_TRUE;
774135446Strhodes	maybeshutdown();
775135446Strhodes}
776135446Strhodes
777135446Strhodesstatic void
778135446Strhodessetup_system(void) {
779135446Strhodes	isc_result_t result;
780135446Strhodes	isc_sockaddr_t bind_any, bind_any6;
781135446Strhodes	lwres_result_t lwresult;
782135446Strhodes	unsigned int attrs, attrmask;
783135446Strhodes	int i;
784193149Sdougb	isc_logconfig_t *logconfig = NULL;
785135446Strhodes
786135446Strhodes	ddebug("setup_system()");
787135446Strhodes
788135446Strhodes	dns_result_register();
789135446Strhodes
790135446Strhodes	result = isc_net_probeipv4();
791135446Strhodes	if (result == ISC_R_SUCCESS)
792135446Strhodes		have_ipv4 = ISC_TRUE;
793135446Strhodes
794135446Strhodes	result = isc_net_probeipv6();
795135446Strhodes	if (result == ISC_R_SUCCESS)
796135446Strhodes		have_ipv6 = ISC_TRUE;
797135446Strhodes
798135446Strhodes	if (!have_ipv4 && !have_ipv6)
799135446Strhodes		fatal("could not find either IPv4 or IPv6");
800135446Strhodes
801193149Sdougb	result = isc_log_create(mctx, &lctx, &logconfig);
802193149Sdougb	check_result(result, "isc_log_create");
803135446Strhodes
804193149Sdougb	isc_log_setcontext(lctx);
805193149Sdougb	dns_log_init(lctx);
806193149Sdougb	dns_log_setcontext(lctx);
807193149Sdougb
808193149Sdougb	result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
809193149Sdougb	check_result(result, "isc_log_usechannel");
810193149Sdougb
811193149Sdougb	isc_log_setdebuglevel(lctx, logdebuglevel);
812193149Sdougb
813135446Strhodes	lwresult = lwres_context_create(&lwctx, mctx, mem_alloc, mem_free, 1);
814135446Strhodes	if (lwresult != LWRES_R_SUCCESS)
815135446Strhodes		fatal("lwres_context_create failed");
816135446Strhodes
817135446Strhodes	(void)lwres_conf_parse(lwctx, RESOLV_CONF);
818135446Strhodes	lwconf = lwres_conf_get(lwctx);
819135446Strhodes
820135446Strhodes	ns_total = lwconf->nsnext;
821135446Strhodes	if (ns_total <= 0) {
822135446Strhodes		/* No name servers in resolv.conf; default to loopback. */
823135446Strhodes		struct in_addr localhost;
824135446Strhodes		ns_total = 1;
825135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
826135446Strhodes		if (servers == NULL)
827135446Strhodes			fatal("out of memory");
828135446Strhodes		localhost.s_addr = htonl(INADDR_LOOPBACK);
829224092Sdougb		isc_sockaddr_fromin(&servers[0], &localhost, dnsport);
830135446Strhodes	} else {
831135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
832135446Strhodes		if (servers == NULL)
833135446Strhodes			fatal("out of memory");
834135446Strhodes		for (i = 0; i < ns_total; i++) {
835135446Strhodes			if (lwconf->nameservers[i].family == LWRES_ADDRTYPE_V4) {
836135446Strhodes				struct in_addr in4;
837135446Strhodes				memcpy(&in4, lwconf->nameservers[i].address, 4);
838224092Sdougb				isc_sockaddr_fromin(&servers[i], &in4, dnsport);
839135446Strhodes			} else {
840135446Strhodes				struct in6_addr in6;
841135446Strhodes				memcpy(&in6, lwconf->nameservers[i].address, 16);
842135446Strhodes				isc_sockaddr_fromin6(&servers[i], &in6,
843224092Sdougb						     dnsport);
844135446Strhodes			}
845135446Strhodes		}
846135446Strhodes	}
847135446Strhodes
848193149Sdougb	setup_entropy(mctx, NULL, &entropy);
849135446Strhodes
850193149Sdougb	result = isc_hash_create(mctx, entropy, DNS_NAME_MAXWIRE);
851135446Strhodes	check_result(result, "isc_hash_create");
852135446Strhodes	isc_hash_init();
853135446Strhodes
854193149Sdougb	result = dns_dispatchmgr_create(mctx, entropy, &dispatchmgr);
855135446Strhodes	check_result(result, "dns_dispatchmgr_create");
856135446Strhodes
857135446Strhodes	result = isc_socketmgr_create(mctx, &socketmgr);
858135446Strhodes	check_result(result, "dns_socketmgr_create");
859135446Strhodes
860135446Strhodes	result = isc_timermgr_create(mctx, &timermgr);
861135446Strhodes	check_result(result, "dns_timermgr_create");
862135446Strhodes
863135446Strhodes	result = isc_taskmgr_create(mctx, 1, 0, &taskmgr);
864135446Strhodes	check_result(result, "isc_taskmgr_create");
865135446Strhodes
866135446Strhodes	result = isc_task_create(taskmgr, 0, &global_task);
867135446Strhodes	check_result(result, "isc_task_create");
868135446Strhodes
869135446Strhodes	result = isc_task_onshutdown(global_task, shutdown_program, NULL);
870135446Strhodes	check_result(result, "isc_task_onshutdown");
871135446Strhodes
872193149Sdougb	result = dst_lib_init(mctx, entropy, 0);
873135446Strhodes	check_result(result, "dst_lib_init");
874135446Strhodes	is_dst_up = ISC_TRUE;
875135446Strhodes
876135446Strhodes	attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
877135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
878135446Strhodes
879135446Strhodes	if (have_ipv6) {
880135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
881135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
882135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
883135446Strhodes		isc_sockaddr_any6(&bind_any6);
884135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
885135446Strhodes					     &bind_any6, PACKETSIZE,
886135446Strhodes					     4, 2, 3, 5,
887135446Strhodes					     attrs, attrmask, &dispatchv6);
888135446Strhodes		check_result(result, "dns_dispatch_getudp (v6)");
889135446Strhodes	}
890135446Strhodes
891135446Strhodes	if (have_ipv4) {
892135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
893135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
894135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
895135446Strhodes		isc_sockaddr_any(&bind_any);
896135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
897135446Strhodes					     &bind_any, PACKETSIZE,
898135446Strhodes					     4, 2, 3, 5,
899135446Strhodes					     attrs, attrmask, &dispatchv4);
900135446Strhodes		check_result(result, "dns_dispatch_getudp (v4)");
901135446Strhodes	}
902135446Strhodes
903135446Strhodes	result = dns_requestmgr_create(mctx, timermgr,
904135446Strhodes				       socketmgr, taskmgr, dispatchmgr,
905135446Strhodes				       dispatchv4, dispatchv6, &requestmgr);
906135446Strhodes	check_result(result, "dns_requestmgr_create");
907135446Strhodes
908135446Strhodes	if (keystr != NULL)
909135446Strhodes		setup_keystr();
910224092Sdougb	else if (local_only) {
911224092Sdougb		result = read_sessionkey(mctx, lctx);
912224092Sdougb		if (result != ISC_R_SUCCESS)
913224092Sdougb			fatal("can't read key from %s: %s\n",
914224092Sdougb			      keyfile, isc_result_totext(result));
915224092Sdougb	} else if (keyfile != NULL)
916224092Sdougb		setup_keyfile(mctx, lctx);
917135446Strhodes}
918135446Strhodes
919135446Strhodesstatic void
920135446Strhodesget_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
921135446Strhodes	int count;
922135446Strhodes	isc_result_t result;
923135446Strhodes
924135446Strhodes	isc_app_block();
925135446Strhodes	result = bind9_getaddresses(host, port, sockaddr, 1, &count);
926135446Strhodes	isc_app_unblock();
927135446Strhodes	if (result != ISC_R_SUCCESS)
928135446Strhodes		fatal("couldn't get address for '%s': %s",
929135446Strhodes		      host, isc_result_totext(result));
930135446Strhodes	INSIST(count == 1);
931135446Strhodes}
932135446Strhodes
933224092Sdougb#define PARSE_ARGS_FMT "dDML:y:ghlovk:p:rR::t:u:"
934193149Sdougb
935135446Strhodesstatic void
936193149Sdougbpre_parse_args(int argc, char **argv) {
937135446Strhodes	int ch;
938193149Sdougb
939193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
940193149Sdougb		switch (ch) {
941193149Sdougb		case 'M': /* was -dm */
942193149Sdougb			debugging = ISC_TRUE;
943193149Sdougb			ddebugging = ISC_TRUE;
944193149Sdougb			memdebugging = ISC_TRUE;
945193149Sdougb			isc_mem_debugging = ISC_MEM_DEBUGTRACE |
946193149Sdougb					    ISC_MEM_DEBUGRECORD;
947193149Sdougb			break;
948193149Sdougb
949193149Sdougb		case '?':
950224092Sdougb		case 'h':
951193149Sdougb			if (isc_commandline_option != '?')
952193149Sdougb				fprintf(stderr, "%s: invalid argument -%c\n",
953193149Sdougb					argv[0], isc_commandline_option);
954224092Sdougb			fprintf(stderr, "usage: nsupdate [-dD] [-L level] [-l]"
955193149Sdougb				"[-g | -o | -y keyname:secret | -k keyfile] "
956193149Sdougb				"[-v] [filename]\n");
957193149Sdougb			exit(1);
958193149Sdougb
959193149Sdougb		default:
960193149Sdougb			break;
961193149Sdougb		}
962193149Sdougb	}
963193149Sdougb	isc_commandline_reset = ISC_TRUE;
964193149Sdougb	isc_commandline_index = 1;
965193149Sdougb}
966193149Sdougb
967193149Sdougbstatic void
968193149Sdougbparse_args(int argc, char **argv, isc_mem_t *mctx, isc_entropy_t **ectx) {
969193149Sdougb	int ch;
970193149Sdougb	isc_uint32_t i;
971135446Strhodes	isc_result_t result;
972135446Strhodes
973135446Strhodes	debug("parse_args");
974193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
975135446Strhodes		switch (ch) {
976135446Strhodes		case 'd':
977135446Strhodes			debugging = ISC_TRUE;
978135446Strhodes			break;
979135446Strhodes		case 'D': /* was -dd */
980135446Strhodes			debugging = ISC_TRUE;
981135446Strhodes			ddebugging = ISC_TRUE;
982135446Strhodes			break;
983193149Sdougb		case 'M':
984135446Strhodes			break;
985193149Sdougb		case 'l':
986224092Sdougb			local_only = ISC_TRUE;
987224092Sdougb			break;
988224092Sdougb		case 'L':
989193149Sdougb			result = isc_parse_uint32(&i, isc_commandline_argument,
990193149Sdougb						  10);
991193149Sdougb			if (result != ISC_R_SUCCESS) {
992193149Sdougb				fprintf(stderr, "bad library debug value "
993193149Sdougb					"'%s'\n", isc_commandline_argument);
994193149Sdougb				exit(1);
995193149Sdougb			}
996193149Sdougb			logdebuglevel = i;
997193149Sdougb			break;
998135446Strhodes		case 'y':
999135446Strhodes			keystr = isc_commandline_argument;
1000135446Strhodes			break;
1001135446Strhodes		case 'v':
1002135446Strhodes			usevc = ISC_TRUE;
1003135446Strhodes			break;
1004135446Strhodes		case 'k':
1005135446Strhodes			keyfile = isc_commandline_argument;
1006135446Strhodes			break;
1007193149Sdougb		case 'g':
1008193149Sdougb			usegsstsig = ISC_TRUE;
1009193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
1010193149Sdougb			break;
1011193149Sdougb		case 'o':
1012193149Sdougb			usegsstsig = ISC_TRUE;
1013193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
1014193149Sdougb			break;
1015224092Sdougb		case 'p':
1016224092Sdougb			result = isc_parse_uint16(&dnsport,
1017224092Sdougb						  isc_commandline_argument, 10);
1018224092Sdougb			if (result != ISC_R_SUCCESS) {
1019224092Sdougb				fprintf(stderr, "bad port number "
1020224092Sdougb					"'%s'\n", isc_commandline_argument);
1021224092Sdougb				exit(1);
1022224092Sdougb			}
1023224092Sdougb			break;
1024135446Strhodes		case 't':
1025135446Strhodes			result = isc_parse_uint32(&timeout,
1026135446Strhodes						  isc_commandline_argument, 10);
1027135446Strhodes			if (result != ISC_R_SUCCESS) {
1028135446Strhodes				fprintf(stderr, "bad timeout '%s'\n",						isc_commandline_argument);
1029135446Strhodes				exit(1);
1030135446Strhodes			}
1031135446Strhodes			if (timeout == 0)
1032143731Sdougb				timeout = UINT_MAX;
1033135446Strhodes			break;
1034135446Strhodes		case 'u':
1035135446Strhodes			result = isc_parse_uint32(&udp_timeout,
1036135446Strhodes						  isc_commandline_argument, 10);
1037135446Strhodes			if (result != ISC_R_SUCCESS) {
1038135446Strhodes				fprintf(stderr, "bad udp timeout '%s'\n",						isc_commandline_argument);
1039135446Strhodes				exit(1);
1040135446Strhodes			}
1041135446Strhodes			if (udp_timeout == 0)
1042143731Sdougb				udp_timeout = UINT_MAX;
1043135446Strhodes			break;
1044135446Strhodes		case 'r':
1045135446Strhodes			result = isc_parse_uint32(&udp_retries,
1046135446Strhodes						  isc_commandline_argument, 10);
1047135446Strhodes			if (result != ISC_R_SUCCESS) {
1048135446Strhodes				fprintf(stderr, "bad udp retries '%s'\n",						isc_commandline_argument);
1049135446Strhodes				exit(1);
1050135446Strhodes			}
1051135446Strhodes			break;
1052193149Sdougb
1053193149Sdougb		case 'R':
1054193149Sdougb			setup_entropy(mctx, isc_commandline_argument, ectx);
1055193149Sdougb			break;
1056193149Sdougb
1057135446Strhodes		default:
1058193149Sdougb			fprintf(stderr, "%s: unhandled option: %c\n",
1059193149Sdougb				argv[0], isc_commandline_option);
1060135446Strhodes			exit(1);
1061135446Strhodes		}
1062135446Strhodes	}
1063135446Strhodes	if (keyfile != NULL && keystr != NULL) {
1064135446Strhodes		fprintf(stderr, "%s: cannot specify both -k and -y\n",
1065135446Strhodes			argv[0]);
1066135446Strhodes		exit(1);
1067135446Strhodes	}
1068135446Strhodes
1069224092Sdougb	if (local_only) {
1070224092Sdougb		struct in_addr localhost;
1071224092Sdougb
1072224092Sdougb		if (keyfile == NULL)
1073224092Sdougb			keyfile = SESSION_KEYFILE;
1074224092Sdougb
1075224092Sdougb		if (userserver == NULL) {
1076224092Sdougb			userserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1077224092Sdougb			if (userserver == NULL)
1078224092Sdougb				fatal("out of memory");
1079224092Sdougb		}
1080224092Sdougb
1081224092Sdougb		localhost.s_addr = htonl(INADDR_LOOPBACK);
1082224092Sdougb		isc_sockaddr_fromin(userserver, &localhost, dnsport);
1083224092Sdougb	}
1084224092Sdougb
1085193149Sdougb#ifdef GSSAPI
1086193149Sdougb	if (usegsstsig && (keyfile != NULL || keystr != NULL)) {
1087193149Sdougb		fprintf(stderr, "%s: cannot specify -g with -k or -y\n",
1088193149Sdougb			argv[0]);
1089193149Sdougb		exit(1);
1090193149Sdougb	}
1091193149Sdougb#else
1092193149Sdougb	if (usegsstsig) {
1093224092Sdougb		fprintf(stderr, "%s: cannot specify -g	or -o, " \
1094193149Sdougb			"program not linked with GSS API Library\n",
1095193149Sdougb			argv[0]);
1096193149Sdougb		exit(1);
1097193149Sdougb	}
1098193149Sdougb#endif
1099193149Sdougb
1100135446Strhodes	if (argv[isc_commandline_index] != NULL) {
1101135446Strhodes		if (strcmp(argv[isc_commandline_index], "-") == 0) {
1102135446Strhodes			input = stdin;
1103135446Strhodes		} else {
1104135446Strhodes			result = isc_stdio_open(argv[isc_commandline_index],
1105135446Strhodes						"r", &input);
1106135446Strhodes			if (result != ISC_R_SUCCESS) {
1107135446Strhodes				fprintf(stderr, "could not open '%s': %s\n",
1108135446Strhodes					argv[isc_commandline_index],
1109135446Strhodes					isc_result_totext(result));
1110135446Strhodes				exit(1);
1111135446Strhodes			}
1112135446Strhodes		}
1113135446Strhodes		interactive = ISC_FALSE;
1114135446Strhodes	}
1115135446Strhodes}
1116135446Strhodes
1117135446Strhodesstatic isc_uint16_t
1118135446Strhodesparse_name(char **cmdlinep, dns_message_t *msg, dns_name_t **namep) {
1119135446Strhodes	isc_result_t result;
1120135446Strhodes	char *word;
1121135446Strhodes	isc_buffer_t *namebuf = NULL;
1122135446Strhodes	isc_buffer_t source;
1123135446Strhodes
1124135446Strhodes	word = nsu_strsep(cmdlinep, " \t\r\n");
1125135446Strhodes	if (*word == 0) {
1126135446Strhodes		fprintf(stderr, "could not read owner name\n");
1127135446Strhodes		return (STATUS_SYNTAX);
1128135446Strhodes	}
1129135446Strhodes
1130135446Strhodes	result = dns_message_gettempname(msg, namep);
1131135446Strhodes	check_result(result, "dns_message_gettempname");
1132135446Strhodes	result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
1133135446Strhodes	check_result(result, "isc_buffer_allocate");
1134135446Strhodes	dns_name_init(*namep, NULL);
1135135446Strhodes	dns_name_setbuffer(*namep, namebuf);
1136135446Strhodes	dns_message_takebuffer(msg, &namebuf);
1137135446Strhodes	isc_buffer_init(&source, word, strlen(word));
1138135446Strhodes	isc_buffer_add(&source, strlen(word));
1139224092Sdougb	result = dns_name_fromtext(*namep, &source, dns_rootname, 0, NULL);
1140135446Strhodes	check_result(result, "dns_name_fromtext");
1141135446Strhodes	isc_buffer_invalidate(&source);
1142135446Strhodes	return (STATUS_MORE);
1143135446Strhodes}
1144135446Strhodes
1145135446Strhodesstatic isc_uint16_t
1146135446Strhodesparse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass,
1147135446Strhodes	    dns_rdatatype_t rdatatype, dns_message_t *msg,
1148135446Strhodes	    dns_rdata_t *rdata)
1149135446Strhodes{
1150135446Strhodes	char *cmdline = *cmdlinep;
1151135446Strhodes	isc_buffer_t source, *buf = NULL, *newbuf = NULL;
1152135446Strhodes	isc_region_t r;
1153135446Strhodes	isc_lex_t *lex = NULL;
1154135446Strhodes	dns_rdatacallbacks_t callbacks;
1155135446Strhodes	isc_result_t result;
1156135446Strhodes
1157135446Strhodes	while (*cmdline != 0 && isspace((unsigned char)*cmdline))
1158135446Strhodes		cmdline++;
1159135446Strhodes
1160135446Strhodes	if (*cmdline != 0) {
1161135446Strhodes		dns_rdatacallbacks_init(&callbacks);
1162135446Strhodes		result = isc_lex_create(mctx, strlen(cmdline), &lex);
1163135446Strhodes		check_result(result, "isc_lex_create");
1164135446Strhodes		isc_buffer_init(&source, cmdline, strlen(cmdline));
1165135446Strhodes		isc_buffer_add(&source, strlen(cmdline));
1166135446Strhodes		result = isc_lex_openbuffer(lex, &source);
1167135446Strhodes		check_result(result, "isc_lex_openbuffer");
1168135446Strhodes		result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
1169135446Strhodes		check_result(result, "isc_buffer_allocate");
1170193149Sdougb		result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex,
1171135446Strhodes					    dns_rootname, 0, mctx, buf,
1172135446Strhodes					    &callbacks);
1173135446Strhodes		isc_lex_destroy(&lex);
1174135446Strhodes		if (result == ISC_R_SUCCESS) {
1175135446Strhodes			isc_buffer_usedregion(buf, &r);
1176135446Strhodes			result = isc_buffer_allocate(mctx, &newbuf, r.length);
1177135446Strhodes			check_result(result, "isc_buffer_allocate");
1178135446Strhodes			isc_buffer_putmem(newbuf, r.base, r.length);
1179135446Strhodes			isc_buffer_usedregion(newbuf, &r);
1180135446Strhodes			dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
1181135446Strhodes			isc_buffer_free(&buf);
1182135446Strhodes			dns_message_takebuffer(msg, &newbuf);
1183135446Strhodes		} else {
1184135446Strhodes			fprintf(stderr, "invalid rdata format: %s\n",
1185135446Strhodes				isc_result_totext(result));
1186135446Strhodes			isc_buffer_free(&buf);
1187135446Strhodes			return (STATUS_SYNTAX);
1188135446Strhodes		}
1189135446Strhodes	} else {
1190135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1191135446Strhodes	}
1192135446Strhodes	*cmdlinep = cmdline;
1193135446Strhodes	return (STATUS_MORE);
1194135446Strhodes}
1195135446Strhodes
1196135446Strhodesstatic isc_uint16_t
1197135446Strhodesmake_prereq(char *cmdline, isc_boolean_t ispositive, isc_boolean_t isrrset) {
1198135446Strhodes	isc_result_t result;
1199135446Strhodes	char *word;
1200135446Strhodes	dns_name_t *name = NULL;
1201135446Strhodes	isc_textregion_t region;
1202135446Strhodes	dns_rdataset_t *rdataset = NULL;
1203135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1204135446Strhodes	dns_rdataclass_t rdataclass;
1205135446Strhodes	dns_rdatatype_t rdatatype;
1206135446Strhodes	dns_rdata_t *rdata = NULL;
1207135446Strhodes	isc_uint16_t retval;
1208135446Strhodes
1209135446Strhodes	ddebug("make_prereq()");
1210135446Strhodes
1211135446Strhodes	/*
1212135446Strhodes	 * Read the owner name
1213135446Strhodes	 */
1214135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1215135446Strhodes	if (retval != STATUS_MORE)
1216135446Strhodes		return (retval);
1217135446Strhodes
1218135446Strhodes	/*
1219135446Strhodes	 * If this is an rrset prereq, read the class or type.
1220135446Strhodes	 */
1221135446Strhodes	if (isrrset) {
1222135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1223135446Strhodes		if (*word == 0) {
1224135446Strhodes			fprintf(stderr, "could not read class or type\n");
1225135446Strhodes			goto failure;
1226135446Strhodes		}
1227135446Strhodes		region.base = word;
1228135446Strhodes		region.length = strlen(word);
1229135446Strhodes		result = dns_rdataclass_fromtext(&rdataclass, &region);
1230135446Strhodes		if (result == ISC_R_SUCCESS) {
1231135446Strhodes			if (!setzoneclass(rdataclass)) {
1232135446Strhodes				fprintf(stderr, "class mismatch: %s\n", word);
1233135446Strhodes				goto failure;
1234135446Strhodes			}
1235135446Strhodes			/*
1236135446Strhodes			 * Now read the type.
1237135446Strhodes			 */
1238135446Strhodes			word = nsu_strsep(&cmdline, " \t\r\n");
1239135446Strhodes			if (*word == 0) {
1240135446Strhodes				fprintf(stderr, "could not read type\n");
1241135446Strhodes				goto failure;
1242135446Strhodes			}
1243135446Strhodes			region.base = word;
1244135446Strhodes			region.length = strlen(word);
1245135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1246135446Strhodes			if (result != ISC_R_SUCCESS) {
1247135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1248135446Strhodes				goto failure;
1249135446Strhodes			}
1250135446Strhodes		} else {
1251135446Strhodes			rdataclass = getzoneclass();
1252135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1253135446Strhodes			if (result != ISC_R_SUCCESS) {
1254135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1255135446Strhodes				goto failure;
1256135446Strhodes			}
1257135446Strhodes		}
1258135446Strhodes	} else
1259135446Strhodes		rdatatype = dns_rdatatype_any;
1260135446Strhodes
1261135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1262135446Strhodes	check_result(result, "dns_message_gettemprdata");
1263135446Strhodes
1264193149Sdougb	dns_rdata_init(rdata);
1265135446Strhodes
1266135446Strhodes	if (isrrset && ispositive) {
1267135446Strhodes		retval = parse_rdata(&cmdline, rdataclass, rdatatype,
1268135446Strhodes				     updatemsg, rdata);
1269135446Strhodes		if (retval != STATUS_MORE)
1270135446Strhodes			goto failure;
1271135446Strhodes	} else
1272135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1273135446Strhodes
1274135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1275135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1276135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1277135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1278135446Strhodes	dns_rdatalist_init(rdatalist);
1279135446Strhodes	rdatalist->type = rdatatype;
1280135446Strhodes	if (ispositive) {
1281135446Strhodes		if (isrrset && rdata->data != NULL)
1282135446Strhodes			rdatalist->rdclass = rdataclass;
1283135446Strhodes		else
1284135446Strhodes			rdatalist->rdclass = dns_rdataclass_any;
1285135446Strhodes	} else
1286135446Strhodes		rdatalist->rdclass = dns_rdataclass_none;
1287135446Strhodes	rdatalist->covers = 0;
1288135446Strhodes	rdatalist->ttl = 0;
1289135446Strhodes	rdata->rdclass = rdatalist->rdclass;
1290135446Strhodes	rdata->type = rdatatype;
1291135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1292135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1293135446Strhodes	dns_rdataset_init(rdataset);
1294135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1295135446Strhodes	ISC_LIST_INIT(name->list);
1296135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1297135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_PREREQUISITE);
1298135446Strhodes	return (STATUS_MORE);
1299135446Strhodes
1300135446Strhodes failure:
1301135446Strhodes	if (name != NULL)
1302135446Strhodes		dns_message_puttempname(updatemsg, &name);
1303135446Strhodes	return (STATUS_SYNTAX);
1304135446Strhodes}
1305135446Strhodes
1306135446Strhodesstatic isc_uint16_t
1307135446Strhodesevaluate_prereq(char *cmdline) {
1308135446Strhodes	char *word;
1309135446Strhodes	isc_boolean_t ispositive, isrrset;
1310135446Strhodes
1311135446Strhodes	ddebug("evaluate_prereq()");
1312135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1313135446Strhodes	if (*word == 0) {
1314135446Strhodes		fprintf(stderr, "could not read operation code\n");
1315135446Strhodes		return (STATUS_SYNTAX);
1316135446Strhodes	}
1317135446Strhodes	if (strcasecmp(word, "nxdomain") == 0) {
1318135446Strhodes		ispositive = ISC_FALSE;
1319135446Strhodes		isrrset = ISC_FALSE;
1320135446Strhodes	} else if (strcasecmp(word, "yxdomain") == 0) {
1321135446Strhodes		ispositive = ISC_TRUE;
1322135446Strhodes		isrrset = ISC_FALSE;
1323135446Strhodes	} else if (strcasecmp(word, "nxrrset") == 0) {
1324135446Strhodes		ispositive = ISC_FALSE;
1325135446Strhodes		isrrset = ISC_TRUE;
1326135446Strhodes	} else if (strcasecmp(word, "yxrrset") == 0) {
1327135446Strhodes		ispositive = ISC_TRUE;
1328135446Strhodes		isrrset = ISC_TRUE;
1329135446Strhodes	} else {
1330135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1331135446Strhodes		return (STATUS_SYNTAX);
1332135446Strhodes	}
1333135446Strhodes	return (make_prereq(cmdline, ispositive, isrrset));
1334135446Strhodes}
1335135446Strhodes
1336135446Strhodesstatic isc_uint16_t
1337135446Strhodesevaluate_server(char *cmdline) {
1338135446Strhodes	char *word, *server;
1339135446Strhodes	long port;
1340135446Strhodes
1341224092Sdougb	if (local_only) {
1342224092Sdougb		fprintf(stderr, "cannot reset server in localhost-only mode\n");
1343224092Sdougb		return (STATUS_SYNTAX);
1344224092Sdougb	}
1345224092Sdougb
1346135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1347135446Strhodes	if (*word == 0) {
1348135446Strhodes		fprintf(stderr, "could not read server name\n");
1349135446Strhodes		return (STATUS_SYNTAX);
1350135446Strhodes	}
1351135446Strhodes	server = word;
1352135446Strhodes
1353135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1354135446Strhodes	if (*word == 0)
1355224092Sdougb		port = dnsport;
1356135446Strhodes	else {
1357135446Strhodes		char *endp;
1358135446Strhodes		port = strtol(word, &endp, 10);
1359135446Strhodes		if (*endp != 0) {
1360135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1361135446Strhodes			return (STATUS_SYNTAX);
1362135446Strhodes		} else if (port < 1 || port > 65535) {
1363135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1364135446Strhodes				"(1 to 65535)\n", word);
1365135446Strhodes			return (STATUS_SYNTAX);
1366135446Strhodes		}
1367135446Strhodes	}
1368135446Strhodes
1369135446Strhodes	if (userserver == NULL) {
1370135446Strhodes		userserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1371135446Strhodes		if (userserver == NULL)
1372135446Strhodes			fatal("out of memory");
1373135446Strhodes	}
1374135446Strhodes
1375135446Strhodes	get_address(server, (in_port_t)port, userserver);
1376135446Strhodes
1377135446Strhodes	return (STATUS_MORE);
1378135446Strhodes}
1379135446Strhodes
1380135446Strhodesstatic isc_uint16_t
1381135446Strhodesevaluate_local(char *cmdline) {
1382135446Strhodes	char *word, *local;
1383135446Strhodes	long port;
1384135446Strhodes	struct in_addr in4;
1385135446Strhodes	struct in6_addr in6;
1386135446Strhodes
1387135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1388135446Strhodes	if (*word == 0) {
1389135446Strhodes		fprintf(stderr, "could not read server name\n");
1390135446Strhodes		return (STATUS_SYNTAX);
1391135446Strhodes	}
1392135446Strhodes	local = word;
1393135446Strhodes
1394135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1395135446Strhodes	if (*word == 0)
1396135446Strhodes		port = 0;
1397135446Strhodes	else {
1398135446Strhodes		char *endp;
1399135446Strhodes		port = strtol(word, &endp, 10);
1400135446Strhodes		if (*endp != 0) {
1401135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1402135446Strhodes			return (STATUS_SYNTAX);
1403135446Strhodes		} else if (port < 1 || port > 65535) {
1404135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1405135446Strhodes				"(1 to 65535)\n", word);
1406135446Strhodes			return (STATUS_SYNTAX);
1407135446Strhodes		}
1408135446Strhodes	}
1409135446Strhodes
1410135446Strhodes	if (localaddr == NULL) {
1411135446Strhodes		localaddr = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1412135446Strhodes		if (localaddr == NULL)
1413135446Strhodes			fatal("out of memory");
1414135446Strhodes	}
1415135446Strhodes
1416135446Strhodes	if (have_ipv6 && inet_pton(AF_INET6, local, &in6) == 1)
1417135446Strhodes		isc_sockaddr_fromin6(localaddr, &in6, (in_port_t)port);
1418135446Strhodes	else if (have_ipv4 && inet_pton(AF_INET, local, &in4) == 1)
1419135446Strhodes		isc_sockaddr_fromin(localaddr, &in4, (in_port_t)port);
1420135446Strhodes	else {
1421135446Strhodes		fprintf(stderr, "invalid address %s", local);
1422135446Strhodes		return (STATUS_SYNTAX);
1423135446Strhodes	}
1424135446Strhodes
1425135446Strhodes	return (STATUS_MORE);
1426135446Strhodes}
1427135446Strhodes
1428135446Strhodesstatic isc_uint16_t
1429135446Strhodesevaluate_key(char *cmdline) {
1430135446Strhodes	char *namestr;
1431135446Strhodes	char *secretstr;
1432135446Strhodes	isc_buffer_t b;
1433135446Strhodes	isc_result_t result;
1434135446Strhodes	dns_fixedname_t fkeyname;
1435135446Strhodes	dns_name_t *keyname;
1436135446Strhodes	int secretlen;
1437135446Strhodes	unsigned char *secret = NULL;
1438135446Strhodes	isc_buffer_t secretbuf;
1439170222Sdougb	dns_name_t *hmacname = NULL;
1440170222Sdougb	isc_uint16_t digestbits = 0;
1441170222Sdougb	char *n;
1442135446Strhodes
1443135446Strhodes	namestr = nsu_strsep(&cmdline, " \t\r\n");
1444135446Strhodes	if (*namestr == 0) {
1445135446Strhodes		fprintf(stderr, "could not read key name\n");
1446135446Strhodes		return (STATUS_SYNTAX);
1447135446Strhodes	}
1448135446Strhodes
1449135446Strhodes	dns_fixedname_init(&fkeyname);
1450135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
1451135446Strhodes
1452170222Sdougb	n = strchr(namestr, ':');
1453170222Sdougb	if (n != NULL) {
1454170222Sdougb		digestbits = parse_hmac(&hmacname, namestr, n - namestr);
1455170222Sdougb		namestr = n + 1;
1456170222Sdougb	} else
1457170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
1458170222Sdougb
1459135446Strhodes	isc_buffer_init(&b, namestr, strlen(namestr));
1460135446Strhodes	isc_buffer_add(&b, strlen(namestr));
1461224092Sdougb	result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL);
1462135446Strhodes	if (result != ISC_R_SUCCESS) {
1463135446Strhodes		fprintf(stderr, "could not parse key name\n");
1464135446Strhodes		return (STATUS_SYNTAX);
1465135446Strhodes	}
1466135446Strhodes
1467135446Strhodes	secretstr = nsu_strsep(&cmdline, "\r\n");
1468135446Strhodes	if (*secretstr == 0) {
1469135446Strhodes		fprintf(stderr, "could not read key secret\n");
1470135446Strhodes		return (STATUS_SYNTAX);
1471135446Strhodes	}
1472135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
1473135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
1474135446Strhodes	if (secret == NULL)
1475135446Strhodes		fatal("out of memory");
1476186462Sdougb
1477135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
1478135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
1479135446Strhodes	if (result != ISC_R_SUCCESS) {
1480135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
1481135446Strhodes			secretstr, isc_result_totext(result));
1482135446Strhodes		isc_mem_free(mctx, secret);
1483135446Strhodes		return (STATUS_SYNTAX);
1484135446Strhodes	}
1485135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
1486135446Strhodes
1487135446Strhodes	if (tsigkey != NULL)
1488135446Strhodes		dns_tsigkey_detach(&tsigkey);
1489170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
1490218384Sdougb				    ISC_FALSE, NULL, 0, 0, mctx, NULL,
1491170222Sdougb				    &tsigkey);
1492135446Strhodes	isc_mem_free(mctx, secret);
1493135446Strhodes	if (result != ISC_R_SUCCESS) {
1494135446Strhodes		fprintf(stderr, "could not create key from %s %s: %s\n",
1495135446Strhodes			namestr, secretstr, dns_result_totext(result));
1496135446Strhodes		return (STATUS_SYNTAX);
1497135446Strhodes	}
1498170222Sdougb	dst_key_setbits(tsigkey->key, digestbits);
1499135446Strhodes	return (STATUS_MORE);
1500135446Strhodes}
1501135446Strhodes
1502135446Strhodesstatic isc_uint16_t
1503135446Strhodesevaluate_zone(char *cmdline) {
1504135446Strhodes	char *word;
1505135446Strhodes	isc_buffer_t b;
1506135446Strhodes	isc_result_t result;
1507135446Strhodes
1508135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1509135446Strhodes	if (*word == 0) {
1510135446Strhodes		fprintf(stderr, "could not read zone name\n");
1511135446Strhodes		return (STATUS_SYNTAX);
1512135446Strhodes	}
1513135446Strhodes
1514135446Strhodes	dns_fixedname_init(&fuserzone);
1515135446Strhodes	userzone = dns_fixedname_name(&fuserzone);
1516135446Strhodes	isc_buffer_init(&b, word, strlen(word));
1517135446Strhodes	isc_buffer_add(&b, strlen(word));
1518224092Sdougb	result = dns_name_fromtext(userzone, &b, dns_rootname, 0, NULL);
1519135446Strhodes	if (result != ISC_R_SUCCESS) {
1520135446Strhodes		userzone = NULL; /* Lest it point to an invalid name */
1521135446Strhodes		fprintf(stderr, "could not parse zone name\n");
1522135446Strhodes		return (STATUS_SYNTAX);
1523135446Strhodes	}
1524135446Strhodes
1525135446Strhodes	return (STATUS_MORE);
1526135446Strhodes}
1527135446Strhodes
1528135446Strhodesstatic isc_uint16_t
1529218384Sdougbevaluate_realm(char *cmdline) {
1530218384Sdougb#ifdef GSSAPI
1531218384Sdougb	char *word;
1532218384Sdougb	char buf[1024];
1533218384Sdougb
1534218384Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
1535218384Sdougb	if (*word == 0) {
1536218384Sdougb		if (realm != NULL)
1537218384Sdougb			isc_mem_free(mctx, realm);
1538218384Sdougb		realm = NULL;
1539218384Sdougb		return (STATUS_MORE);
1540218384Sdougb	}
1541218384Sdougb
1542218384Sdougb	snprintf(buf, sizeof(buf), "@%s", word);
1543218384Sdougb	realm = isc_mem_strdup(mctx, buf);
1544218384Sdougb	if (realm == NULL)
1545218384Sdougb		fatal("out of memory");
1546218384Sdougb	return (STATUS_MORE);
1547218384Sdougb#else
1548218384Sdougb	UNUSED(cmdline);
1549218384Sdougb	return (STATUS_SYNTAX);
1550218384Sdougb#endif
1551218384Sdougb}
1552218384Sdougb
1553218384Sdougbstatic isc_uint16_t
1554193149Sdougbevaluate_ttl(char *cmdline) {
1555193149Sdougb	char *word;
1556193149Sdougb	isc_result_t result;
1557193149Sdougb	isc_uint32_t ttl;
1558193149Sdougb
1559193149Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
1560193149Sdougb	if (*word == 0) {
1561193149Sdougb		fprintf(stderr, "could not ttl\n");
1562193149Sdougb		return (STATUS_SYNTAX);
1563193149Sdougb	}
1564193149Sdougb
1565193149Sdougb	if (!strcasecmp(word, "none")) {
1566193149Sdougb		default_ttl = 0;
1567193149Sdougb		default_ttl_set = ISC_FALSE;
1568193149Sdougb		return (STATUS_MORE);
1569193149Sdougb	}
1570193149Sdougb
1571193149Sdougb	result = isc_parse_uint32(&ttl, word, 10);
1572193149Sdougb	if (result != ISC_R_SUCCESS)
1573193149Sdougb		return (STATUS_SYNTAX);
1574193149Sdougb
1575193149Sdougb	if (ttl > TTL_MAX) {
1576193149Sdougb		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1577193149Sdougb			word, TTL_MAX);
1578193149Sdougb		return (STATUS_SYNTAX);
1579193149Sdougb	}
1580193149Sdougb	default_ttl = ttl;
1581193149Sdougb	default_ttl_set = ISC_TRUE;
1582193149Sdougb
1583193149Sdougb	return (STATUS_MORE);
1584193149Sdougb}
1585193149Sdougb
1586193149Sdougbstatic isc_uint16_t
1587135446Strhodesevaluate_class(char *cmdline) {
1588135446Strhodes	char *word;
1589135446Strhodes	isc_textregion_t r;
1590135446Strhodes	isc_result_t result;
1591135446Strhodes	dns_rdataclass_t rdclass;
1592135446Strhodes
1593135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1594135446Strhodes	if (*word == 0) {
1595135446Strhodes		fprintf(stderr, "could not read class name\n");
1596135446Strhodes		return (STATUS_SYNTAX);
1597135446Strhodes	}
1598135446Strhodes
1599135446Strhodes	r.base = word;
1600186462Sdougb	r.length = strlen(word);
1601186462Sdougb	result = dns_rdataclass_fromtext(&rdclass, &r);
1602135446Strhodes	if (result != ISC_R_SUCCESS) {
1603135446Strhodes		fprintf(stderr, "could not parse class name: %s\n", word);
1604135446Strhodes		return (STATUS_SYNTAX);
1605135446Strhodes	}
1606135446Strhodes	switch (rdclass) {
1607135446Strhodes	case dns_rdataclass_none:
1608135446Strhodes	case dns_rdataclass_any:
1609135446Strhodes	case dns_rdataclass_reserved0:
1610135446Strhodes		fprintf(stderr, "bad default class: %s\n", word);
1611135446Strhodes		return (STATUS_SYNTAX);
1612135446Strhodes	default:
1613135446Strhodes		defaultclass = rdclass;
1614135446Strhodes	}
1615135446Strhodes
1616135446Strhodes	return (STATUS_MORE);
1617135446Strhodes}
1618135446Strhodes
1619135446Strhodesstatic isc_uint16_t
1620135446Strhodesupdate_addordelete(char *cmdline, isc_boolean_t isdelete) {
1621135446Strhodes	isc_result_t result;
1622135446Strhodes	dns_name_t *name = NULL;
1623135446Strhodes	isc_uint32_t ttl;
1624135446Strhodes	char *word;
1625135446Strhodes	dns_rdataclass_t rdataclass;
1626135446Strhodes	dns_rdatatype_t rdatatype;
1627135446Strhodes	dns_rdata_t *rdata = NULL;
1628135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1629135446Strhodes	dns_rdataset_t *rdataset = NULL;
1630135446Strhodes	isc_textregion_t region;
1631135446Strhodes	isc_uint16_t retval;
1632135446Strhodes
1633135446Strhodes	ddebug("update_addordelete()");
1634135446Strhodes
1635135446Strhodes	/*
1636135446Strhodes	 * Read the owner name.
1637135446Strhodes	 */
1638135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1639135446Strhodes	if (retval != STATUS_MORE)
1640135446Strhodes		return (retval);
1641135446Strhodes
1642135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1643135446Strhodes	check_result(result, "dns_message_gettemprdata");
1644135446Strhodes
1645193149Sdougb	dns_rdata_init(rdata);
1646135446Strhodes
1647135446Strhodes	/*
1648135446Strhodes	 * If this is an add, read the TTL and verify that it's in range.
1649135446Strhodes	 * If it's a delete, ignore a TTL if present (for compatibility).
1650135446Strhodes	 */
1651135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1652135446Strhodes	if (*word == 0) {
1653135446Strhodes		if (!isdelete) {
1654135446Strhodes			fprintf(stderr, "could not read owner ttl\n");
1655135446Strhodes			goto failure;
1656135446Strhodes		}
1657135446Strhodes		else {
1658135446Strhodes			ttl = 0;
1659135446Strhodes			rdataclass = dns_rdataclass_any;
1660135446Strhodes			rdatatype = dns_rdatatype_any;
1661135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1662135446Strhodes			goto doneparsing;
1663135446Strhodes		}
1664135446Strhodes	}
1665135446Strhodes	result = isc_parse_uint32(&ttl, word, 10);
1666135446Strhodes	if (result != ISC_R_SUCCESS) {
1667135446Strhodes		if (isdelete) {
1668135446Strhodes			ttl = 0;
1669135446Strhodes			goto parseclass;
1670193149Sdougb		} else if (default_ttl_set) {
1671193149Sdougb			ttl = default_ttl;
1672193149Sdougb			goto parseclass;
1673135446Strhodes		} else {
1674135446Strhodes			fprintf(stderr, "ttl '%s': %s\n", word,
1675135446Strhodes				isc_result_totext(result));
1676135446Strhodes			goto failure;
1677135446Strhodes		}
1678135446Strhodes	}
1679135446Strhodes
1680135446Strhodes	if (isdelete)
1681135446Strhodes		ttl = 0;
1682135446Strhodes	else if (ttl > TTL_MAX) {
1683135446Strhodes		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1684135446Strhodes			word, TTL_MAX);
1685135446Strhodes		goto failure;
1686135446Strhodes	}
1687135446Strhodes
1688135446Strhodes	/*
1689135446Strhodes	 * Read the class or type.
1690135446Strhodes	 */
1691135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1692135446Strhodes parseclass:
1693135446Strhodes	if (*word == 0) {
1694135446Strhodes		if (isdelete) {
1695135446Strhodes			rdataclass = dns_rdataclass_any;
1696135446Strhodes			rdatatype = dns_rdatatype_any;
1697135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1698135446Strhodes			goto doneparsing;
1699135446Strhodes		} else {
1700135446Strhodes			fprintf(stderr, "could not read class or type\n");
1701135446Strhodes			goto failure;
1702135446Strhodes		}
1703135446Strhodes	}
1704135446Strhodes	region.base = word;
1705135446Strhodes	region.length = strlen(word);
1706193149Sdougb	rdataclass = dns_rdataclass_any;
1707135446Strhodes	result = dns_rdataclass_fromtext(&rdataclass, &region);
1708193149Sdougb	if (result == ISC_R_SUCCESS && rdataclass != dns_rdataclass_any) {
1709135446Strhodes		if (!setzoneclass(rdataclass)) {
1710135446Strhodes			fprintf(stderr, "class mismatch: %s\n", word);
1711135446Strhodes			goto failure;
1712135446Strhodes		}
1713135446Strhodes		/*
1714135446Strhodes		 * Now read the type.
1715135446Strhodes		 */
1716135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1717135446Strhodes		if (*word == 0) {
1718135446Strhodes			if (isdelete) {
1719135446Strhodes				rdataclass = dns_rdataclass_any;
1720135446Strhodes				rdatatype = dns_rdatatype_any;
1721135446Strhodes				rdata->flags = DNS_RDATA_UPDATE;
1722135446Strhodes				goto doneparsing;
1723135446Strhodes			} else {
1724135446Strhodes				fprintf(stderr, "could not read type\n");
1725135446Strhodes				goto failure;
1726135446Strhodes			}
1727135446Strhodes		}
1728135446Strhodes		region.base = word;
1729135446Strhodes		region.length = strlen(word);
1730135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1731135446Strhodes		if (result != ISC_R_SUCCESS) {
1732135446Strhodes			fprintf(stderr, "'%s' is not a valid type: %s\n",
1733135446Strhodes				word, isc_result_totext(result));
1734135446Strhodes			goto failure;
1735135446Strhodes		}
1736135446Strhodes	} else {
1737135446Strhodes		rdataclass = getzoneclass();
1738135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1739135446Strhodes		if (result != ISC_R_SUCCESS) {
1740135446Strhodes			fprintf(stderr, "'%s' is not a valid class or type: "
1741135446Strhodes				"%s\n", word, isc_result_totext(result));
1742135446Strhodes			goto failure;
1743135446Strhodes		}
1744135446Strhodes	}
1745135446Strhodes
1746135446Strhodes	retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg,
1747135446Strhodes			     rdata);
1748135446Strhodes	if (retval != STATUS_MORE)
1749135446Strhodes		goto failure;
1750135446Strhodes
1751135446Strhodes	if (isdelete) {
1752135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
1753135446Strhodes			rdataclass = dns_rdataclass_any;
1754135446Strhodes		else
1755135446Strhodes			rdataclass = dns_rdataclass_none;
1756135446Strhodes	} else {
1757135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
1758135446Strhodes			fprintf(stderr, "could not read rdata\n");
1759135446Strhodes			goto failure;
1760135446Strhodes		}
1761135446Strhodes	}
1762135446Strhodes
1763135446Strhodes doneparsing:
1764135446Strhodes
1765135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1766135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1767135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1768135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1769135446Strhodes	dns_rdatalist_init(rdatalist);
1770135446Strhodes	rdatalist->type = rdatatype;
1771135446Strhodes	rdatalist->rdclass = rdataclass;
1772135446Strhodes	rdatalist->covers = rdatatype;
1773135446Strhodes	rdatalist->ttl = (dns_ttl_t)ttl;
1774135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1775135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1776135446Strhodes	dns_rdataset_init(rdataset);
1777135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1778135446Strhodes	ISC_LIST_INIT(name->list);
1779135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1780135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_UPDATE);
1781135446Strhodes	return (STATUS_MORE);
1782135446Strhodes
1783135446Strhodes failure:
1784135446Strhodes	if (name != NULL)
1785135446Strhodes		dns_message_puttempname(updatemsg, &name);
1786186462Sdougb	dns_message_puttemprdata(updatemsg, &rdata);
1787135446Strhodes	return (STATUS_SYNTAX);
1788135446Strhodes}
1789135446Strhodes
1790135446Strhodesstatic isc_uint16_t
1791135446Strhodesevaluate_update(char *cmdline) {
1792135446Strhodes	char *word;
1793135446Strhodes	isc_boolean_t isdelete;
1794135446Strhodes
1795135446Strhodes	ddebug("evaluate_update()");
1796135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1797135446Strhodes	if (*word == 0) {
1798135446Strhodes		fprintf(stderr, "could not read operation code\n");
1799135446Strhodes		return (STATUS_SYNTAX);
1800135446Strhodes	}
1801135446Strhodes	if (strcasecmp(word, "delete") == 0)
1802135446Strhodes		isdelete = ISC_TRUE;
1803135446Strhodes	else if (strcasecmp(word, "add") == 0)
1804135446Strhodes		isdelete = ISC_FALSE;
1805135446Strhodes	else {
1806135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1807135446Strhodes		return (STATUS_SYNTAX);
1808135446Strhodes	}
1809135446Strhodes	return (update_addordelete(cmdline, isdelete));
1810135446Strhodes}
1811135446Strhodes
1812135446Strhodesstatic void
1813170222Sdougbsetzone(dns_name_t *zonename) {
1814170222Sdougb	isc_result_t result;
1815170222Sdougb	dns_name_t *name = NULL;
1816170222Sdougb	dns_rdataset_t *rdataset = NULL;
1817170222Sdougb
1818170222Sdougb	result = dns_message_firstname(updatemsg, DNS_SECTION_ZONE);
1819170222Sdougb	if (result == ISC_R_SUCCESS) {
1820170222Sdougb		dns_message_currentname(updatemsg, DNS_SECTION_ZONE, &name);
1821170222Sdougb		dns_message_removename(updatemsg, name, DNS_SECTION_ZONE);
1822170222Sdougb		for (rdataset = ISC_LIST_HEAD(name->list);
1823170222Sdougb		     rdataset != NULL;
1824170222Sdougb		     rdataset = ISC_LIST_HEAD(name->list)) {
1825170222Sdougb			ISC_LIST_UNLINK(name->list, rdataset, link);
1826170222Sdougb			dns_rdataset_disassociate(rdataset);
1827170222Sdougb			dns_message_puttemprdataset(updatemsg, &rdataset);
1828170222Sdougb		}
1829170222Sdougb		dns_message_puttempname(updatemsg, &name);
1830170222Sdougb	}
1831170222Sdougb
1832170222Sdougb	if (zonename != NULL) {
1833170222Sdougb		result = dns_message_gettempname(updatemsg, &name);
1834170222Sdougb		check_result(result, "dns_message_gettempname");
1835170222Sdougb		dns_name_init(name, NULL);
1836170222Sdougb		dns_name_clone(zonename, name);
1837170222Sdougb		result = dns_message_gettemprdataset(updatemsg, &rdataset);
1838170222Sdougb		check_result(result, "dns_message_gettemprdataset");
1839170222Sdougb		dns_rdataset_makequestion(rdataset, getzoneclass(),
1840170222Sdougb					  dns_rdatatype_soa);
1841170222Sdougb		ISC_LIST_INIT(name->list);
1842170222Sdougb		ISC_LIST_APPEND(name->list, rdataset, link);
1843170222Sdougb		dns_message_addname(updatemsg, name, DNS_SECTION_ZONE);
1844170222Sdougb	}
1845170222Sdougb}
1846170222Sdougb
1847170222Sdougbstatic void
1848193149Sdougbshow_message(FILE *stream, dns_message_t *msg, const char *description) {
1849135446Strhodes	isc_result_t result;
1850135446Strhodes	isc_buffer_t *buf = NULL;
1851135446Strhodes	int bufsz;
1852135446Strhodes
1853135446Strhodes	ddebug("show_message()");
1854170222Sdougb
1855170222Sdougb	setzone(userzone);
1856170222Sdougb
1857135446Strhodes	bufsz = INITTEXT;
1858186462Sdougb	do {
1859135446Strhodes		if (bufsz > MAXTEXT) {
1860135446Strhodes			fprintf(stderr, "could not allocate large enough "
1861135446Strhodes				"buffer to display message\n");
1862135446Strhodes			exit(1);
1863135446Strhodes		}
1864135446Strhodes		if (buf != NULL)
1865135446Strhodes			isc_buffer_free(&buf);
1866135446Strhodes		result = isc_buffer_allocate(mctx, &buf, bufsz);
1867135446Strhodes		check_result(result, "isc_buffer_allocate");
1868135446Strhodes		result = dns_message_totext(msg, style, 0, buf);
1869135446Strhodes		bufsz *= 2;
1870135446Strhodes	} while (result == ISC_R_NOSPACE);
1871135446Strhodes	if (result != ISC_R_SUCCESS) {
1872135446Strhodes		fprintf(stderr, "could not convert message to text format.\n");
1873135446Strhodes		isc_buffer_free(&buf);
1874135446Strhodes		return;
1875135446Strhodes	}
1876193149Sdougb	fprintf(stream, "%s\n%.*s", description,
1877193149Sdougb	       (int)isc_buffer_usedlength(buf), (char*)isc_buffer_base(buf));
1878135446Strhodes	isc_buffer_free(&buf);
1879135446Strhodes}
1880135446Strhodes
1881135446Strhodes
1882135446Strhodesstatic isc_uint16_t
1883135446Strhodesget_next_command(void) {
1884135446Strhodes	char cmdlinebuf[MAXCMD];
1885135446Strhodes	char *cmdline;
1886135446Strhodes	char *word;
1887135446Strhodes
1888135446Strhodes	ddebug("get_next_command()");
1889165071Sdougb	if (interactive) {
1890135446Strhodes		fprintf(stdout, "> ");
1891165071Sdougb		fflush(stdout);
1892165071Sdougb	}
1893135446Strhodes	isc_app_block();
1894135446Strhodes	cmdline = fgets(cmdlinebuf, MAXCMD, input);
1895135446Strhodes	isc_app_unblock();
1896135446Strhodes	if (cmdline == NULL)
1897135446Strhodes		return (STATUS_QUIT);
1898135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1899135446Strhodes
1900135446Strhodes	if (feof(input))
1901135446Strhodes		return (STATUS_QUIT);
1902135446Strhodes	if (*word == 0)
1903135446Strhodes		return (STATUS_SEND);
1904135446Strhodes	if (word[0] == ';')
1905135446Strhodes		return (STATUS_MORE);
1906135446Strhodes	if (strcasecmp(word, "quit") == 0)
1907135446Strhodes		return (STATUS_QUIT);
1908135446Strhodes	if (strcasecmp(word, "prereq") == 0)
1909135446Strhodes		return (evaluate_prereq(cmdline));
1910135446Strhodes	if (strcasecmp(word, "update") == 0)
1911135446Strhodes		return (evaluate_update(cmdline));
1912135446Strhodes	if (strcasecmp(word, "server") == 0)
1913135446Strhodes		return (evaluate_server(cmdline));
1914135446Strhodes	if (strcasecmp(word, "local") == 0)
1915135446Strhodes		return (evaluate_local(cmdline));
1916135446Strhodes	if (strcasecmp(word, "zone") == 0)
1917135446Strhodes		return (evaluate_zone(cmdline));
1918135446Strhodes	if (strcasecmp(word, "class") == 0)
1919135446Strhodes		return (evaluate_class(cmdline));
1920135446Strhodes	if (strcasecmp(word, "send") == 0)
1921135446Strhodes		return (STATUS_SEND);
1922193149Sdougb	if (strcasecmp(word, "debug") == 0) {
1923193149Sdougb		if (debugging)
1924193149Sdougb			ddebugging = ISC_TRUE;
1925193149Sdougb		else
1926193149Sdougb			debugging = ISC_TRUE;
1927193149Sdougb		return (STATUS_MORE);
1928193149Sdougb	}
1929193149Sdougb	if (strcasecmp(word, "ttl") == 0)
1930193149Sdougb		return (evaluate_ttl(cmdline));
1931135446Strhodes	if (strcasecmp(word, "show") == 0) {
1932193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
1933135446Strhodes		return (STATUS_MORE);
1934135446Strhodes	}
1935135446Strhodes	if (strcasecmp(word, "answer") == 0) {
1936135446Strhodes		if (answer != NULL)
1937193149Sdougb			show_message(stdout, answer, "Answer:");
1938135446Strhodes		return (STATUS_MORE);
1939135446Strhodes	}
1940193149Sdougb	if (strcasecmp(word, "key") == 0) {
1941193149Sdougb		usegsstsig = ISC_FALSE;
1942135446Strhodes		return (evaluate_key(cmdline));
1943193149Sdougb	}
1944218384Sdougb	if (strcasecmp(word, "realm") == 0)
1945218384Sdougb		return (evaluate_realm(cmdline));
1946193149Sdougb	if (strcasecmp(word, "gsstsig") == 0) {
1947193149Sdougb#ifdef GSSAPI
1948193149Sdougb		usegsstsig = ISC_TRUE;
1949193149Sdougb		use_win2k_gsstsig = ISC_FALSE;
1950193149Sdougb#else
1951193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1952193149Sdougb#endif
1953193149Sdougb		return (STATUS_MORE);
1954193149Sdougb	}
1955193149Sdougb	if (strcasecmp(word, "oldgsstsig") == 0) {
1956193149Sdougb#ifdef GSSAPI
1957193149Sdougb		usegsstsig = ISC_TRUE;
1958193149Sdougb		use_win2k_gsstsig = ISC_TRUE;
1959193149Sdougb#else
1960193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1961193149Sdougb#endif
1962193149Sdougb		return (STATUS_MORE);
1963193149Sdougb	}
1964193149Sdougb	if (strcasecmp(word, "help") == 0) {
1965193149Sdougb		fprintf(stdout,
1966193149Sdougb"local address [port]      (set local resolver)\n"
1967193149Sdougb"server address [port]     (set master server for zone)\n"
1968193149Sdougb"send                      (send the update request)\n"
1969193149Sdougb"show                      (show the update request)\n"
1970224092Sdougb"answer                    (show the answer to the last request)\n"
1971193149Sdougb"quit                      (quit, any pending update is not sent\n"
1972224092Sdougb"help                      (display this message_\n"
1973193149Sdougb"key [hmac:]keyname secret (use TSIG to sign the request)\n"
1974193149Sdougb"gsstsig                   (use GSS_TSIG to sign the request)\n"
1975193149Sdougb"oldgsstsig                (use Microsoft's GSS_TSIG to sign the request)\n"
1976193149Sdougb"zone name                 (set the zone to be updated)\n"
1977193149Sdougb"class CLASS               (set the zone's DNS class, e.g. IN (default), CH)\n"
1978193149Sdougb"prereq nxdomain name      (does this name not exist)\n"
1979193149Sdougb"prereq yxdomain name      (does this name exist)\n"
1980193149Sdougb"prereq nxrrset ....       (does this RRset exist)\n"
1981193149Sdougb"prereq yxrrset ....       (does this RRset not exist)\n"
1982193149Sdougb"update add ....           (add the given record to the zone)\n"
1983193149Sdougb"update delete ....        (remove the given record(s) from the zone)\n");
1984193149Sdougb		return (STATUS_MORE);
1985193149Sdougb	}
1986135446Strhodes	fprintf(stderr, "incorrect section name: %s\n", word);
1987135446Strhodes	return (STATUS_SYNTAX);
1988135446Strhodes}
1989135446Strhodes
1990135446Strhodesstatic isc_boolean_t
1991135446Strhodesuser_interaction(void) {
1992135446Strhodes	isc_uint16_t result = STATUS_MORE;
1993135446Strhodes
1994135446Strhodes	ddebug("user_interaction()");
1995174187Sdougb	while ((result == STATUS_MORE) || (result == STATUS_SYNTAX)) {
1996135446Strhodes		result = get_next_command();
1997174187Sdougb		if (!interactive && result == STATUS_SYNTAX)
1998174187Sdougb			fatal("syntax error");
1999174187Sdougb	}
2000135446Strhodes	if (result == STATUS_SEND)
2001135446Strhodes		return (ISC_TRUE);
2002135446Strhodes	return (ISC_FALSE);
2003135446Strhodes
2004135446Strhodes}
2005135446Strhodes
2006135446Strhodesstatic void
2007135446Strhodesdone_update(void) {
2008135446Strhodes	isc_event_t *event = global_event;
2009135446Strhodes	ddebug("done_update()");
2010135446Strhodes	isc_task_send(global_task, &event);
2011135446Strhodes}
2012135446Strhodes
2013135446Strhodesstatic void
2014135446Strhodescheck_tsig_error(dns_rdataset_t *rdataset, isc_buffer_t *b) {
2015135446Strhodes	isc_result_t result;
2016135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2017135446Strhodes	dns_rdata_any_tsig_t tsig;
2018135446Strhodes
2019135446Strhodes	result = dns_rdataset_first(rdataset);
2020135446Strhodes	check_result(result, "dns_rdataset_first");
2021135446Strhodes	dns_rdataset_current(rdataset, &rdata);
2022135446Strhodes	result = dns_rdata_tostruct(&rdata, &tsig, NULL);
2023135446Strhodes	check_result(result, "dns_rdata_tostruct");
2024135446Strhodes	if (tsig.error != 0) {
2025135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
2026135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
2027135446Strhodes		isc__buffer_putstr(b, "(" /*)*/);
2028135446Strhodes		result = dns_tsigrcode_totext(tsig.error, b);
2029135446Strhodes		check_result(result, "dns_tsigrcode_totext");
2030135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
2031135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
2032135446Strhodes		isc__buffer_putstr(b,  /*(*/ ")");
2033135446Strhodes	}
2034135446Strhodes}
2035135446Strhodes
2036135446Strhodesstatic void
2037135446Strhodesupdate_completed(isc_task_t *task, isc_event_t *event) {
2038135446Strhodes	dns_requestevent_t *reqev = NULL;
2039135446Strhodes	isc_result_t result;
2040135446Strhodes	dns_request_t *request;
2041135446Strhodes
2042135446Strhodes	UNUSED(task);
2043135446Strhodes
2044135446Strhodes	ddebug("update_completed()");
2045135446Strhodes
2046135446Strhodes	requests--;
2047135446Strhodes
2048135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2049135446Strhodes	reqev = (dns_requestevent_t *)event;
2050135446Strhodes	request = reqev->request;
2051135446Strhodes
2052135446Strhodes	if (shuttingdown) {
2053135446Strhodes		dns_request_destroy(&request);
2054135446Strhodes		isc_event_free(&event);
2055135446Strhodes		maybeshutdown();
2056135446Strhodes		return;
2057135446Strhodes	}
2058135446Strhodes
2059135446Strhodes	if (reqev->result != ISC_R_SUCCESS) {
2060135446Strhodes		fprintf(stderr, "; Communication with server failed: %s\n",
2061135446Strhodes			isc_result_totext(reqev->result));
2062135446Strhodes		seenerror = ISC_TRUE;
2063135446Strhodes		goto done;
2064135446Strhodes	}
2065135446Strhodes
2066135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &answer);
2067135446Strhodes	check_result(result, "dns_message_create");
2068135446Strhodes	result = dns_request_getresponse(request, answer,
2069135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
2070135446Strhodes	switch (result) {
2071135446Strhodes	case ISC_R_SUCCESS:
2072193149Sdougb		if (answer->verify_attempted)
2073193149Sdougb			ddebug("tsig verification successful");
2074135446Strhodes		break;
2075135446Strhodes	case DNS_R_CLOCKSKEW:
2076135446Strhodes	case DNS_R_EXPECTEDTSIG:
2077135446Strhodes	case DNS_R_TSIGERRORSET:
2078135446Strhodes	case DNS_R_TSIGVERIFYFAILURE:
2079135446Strhodes	case DNS_R_UNEXPECTEDTSIG:
2080193149Sdougb	case ISC_R_FAILURE:
2081193149Sdougb#if 0
2082193149Sdougb		if (usegsstsig && answer->rcode == dns_rcode_noerror) {
2083193149Sdougb			/*
2084193149Sdougb			 * For MS DNS that violates RFC 2845, section 4.2
2085193149Sdougb			 */
2086193149Sdougb			break;
2087193149Sdougb		}
2088193149Sdougb#endif
2089135446Strhodes		fprintf(stderr, "; TSIG error with server: %s\n",
2090135446Strhodes			isc_result_totext(result));
2091135446Strhodes		seenerror = ISC_TRUE;
2092135446Strhodes		break;
2093135446Strhodes	default:
2094135446Strhodes		check_result(result, "dns_request_getresponse");
2095135446Strhodes	}
2096135446Strhodes
2097135446Strhodes	if (answer->rcode != dns_rcode_noerror) {
2098135446Strhodes		seenerror = ISC_TRUE;
2099135446Strhodes		if (!debugging) {
2100135446Strhodes			char buf[64];
2101135446Strhodes			isc_buffer_t b;
2102135446Strhodes			dns_rdataset_t *rds;
2103186462Sdougb
2104135446Strhodes			isc_buffer_init(&b, buf, sizeof(buf) - 1);
2105135446Strhodes			result = dns_rcode_totext(answer->rcode, &b);
2106135446Strhodes			check_result(result, "dns_rcode_totext");
2107135446Strhodes			rds = dns_message_gettsig(answer, NULL);
2108135446Strhodes			if (rds != NULL)
2109135446Strhodes				check_tsig_error(rds, &b);
2110135446Strhodes			fprintf(stderr, "update failed: %.*s\n",
2111135446Strhodes				(int)isc_buffer_usedlength(&b), buf);
2112135446Strhodes		}
2113135446Strhodes	}
2114193149Sdougb	if (debugging)
2115193149Sdougb		show_message(stderr, answer, "\nReply from update query:");
2116135446Strhodes
2117135446Strhodes done:
2118135446Strhodes	dns_request_destroy(&request);
2119193149Sdougb	if (usegsstsig) {
2120193149Sdougb		dns_name_free(&tmpzonename, mctx);
2121193149Sdougb		dns_name_free(&restart_master, mctx);
2122193149Sdougb	}
2123135446Strhodes	isc_event_free(&event);
2124135446Strhodes	done_update();
2125135446Strhodes}
2126135446Strhodes
2127135446Strhodesstatic void
2128135446Strhodessend_update(dns_name_t *zonename, isc_sockaddr_t *master,
2129135446Strhodes	    isc_sockaddr_t *srcaddr)
2130135446Strhodes{
2131135446Strhodes	isc_result_t result;
2132135446Strhodes	dns_request_t *request = NULL;
2133224092Sdougb	unsigned int options = DNS_REQUESTOPT_CASE;
2134135446Strhodes
2135135446Strhodes	ddebug("send_update()");
2136135446Strhodes
2137170222Sdougb	setzone(zonename);
2138135446Strhodes
2139135446Strhodes	if (usevc)
2140135446Strhodes		options |= DNS_REQUESTOPT_TCP;
2141135446Strhodes	if (tsigkey == NULL && sig0key != NULL) {
2142135446Strhodes		result = dns_message_setsig0key(updatemsg, sig0key);
2143135446Strhodes		check_result(result, "dns_message_setsig0key");
2144135446Strhodes	}
2145135446Strhodes	if (debugging) {
2146135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2147135446Strhodes
2148135446Strhodes		isc_sockaddr_format(master, addrbuf, sizeof(addrbuf));
2149135446Strhodes		fprintf(stderr, "Sending update to %s\n", addrbuf);
2150135446Strhodes	}
2151193149Sdougb
2152218384Sdougb	/* Windows doesn't like the tsig name to be compressed. */
2153218384Sdougb	if (updatemsg->tsigname)
2154218384Sdougb		updatemsg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2155218384Sdougb
2156135446Strhodes	result = dns_request_createvia3(requestmgr, updatemsg, srcaddr,
2157135446Strhodes					master, options, tsigkey, timeout,
2158135446Strhodes					udp_timeout, udp_retries, global_task,
2159135446Strhodes					update_completed, NULL, &request);
2160135446Strhodes	check_result(result, "dns_request_createvia3");
2161135446Strhodes
2162135446Strhodes	if (debugging)
2163193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
2164135446Strhodes
2165135446Strhodes	requests++;
2166135446Strhodes}
2167135446Strhodes
2168135446Strhodesstatic void
2169135446Strhodesrecvsoa(isc_task_t *task, isc_event_t *event) {
2170135446Strhodes	dns_requestevent_t *reqev = NULL;
2171135446Strhodes	dns_request_t *request = NULL;
2172135446Strhodes	isc_result_t result, eresult;
2173135446Strhodes	dns_message_t *rcvmsg = NULL;
2174135446Strhodes	dns_section_t section;
2175135446Strhodes	dns_name_t *name = NULL;
2176135446Strhodes	dns_rdataset_t *soaset = NULL;
2177135446Strhodes	dns_rdata_soa_t soa;
2178135446Strhodes	dns_rdata_t soarr = DNS_RDATA_INIT;
2179135446Strhodes	int pass = 0;
2180135446Strhodes	dns_name_t master;
2181135446Strhodes	nsu_requestinfo_t *reqinfo;
2182135446Strhodes	dns_message_t *soaquery = NULL;
2183135446Strhodes	isc_sockaddr_t *addr;
2184135446Strhodes	isc_boolean_t seencname = ISC_FALSE;
2185143731Sdougb	dns_name_t tname;
2186143731Sdougb	unsigned int nlabels;
2187135446Strhodes
2188135446Strhodes	UNUSED(task);
2189135446Strhodes
2190135446Strhodes	ddebug("recvsoa()");
2191135446Strhodes
2192135446Strhodes	requests--;
2193186462Sdougb
2194135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2195135446Strhodes	reqev = (dns_requestevent_t *)event;
2196135446Strhodes	request = reqev->request;
2197135446Strhodes	eresult = reqev->result;
2198135446Strhodes	reqinfo = reqev->ev_arg;
2199135446Strhodes	soaquery = reqinfo->msg;
2200135446Strhodes	addr = reqinfo->addr;
2201135446Strhodes
2202135446Strhodes	if (shuttingdown) {
2203135446Strhodes		dns_request_destroy(&request);
2204135446Strhodes		dns_message_destroy(&soaquery);
2205135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2206135446Strhodes		isc_event_free(&event);
2207135446Strhodes		maybeshutdown();
2208135446Strhodes		return;
2209135446Strhodes	}
2210135446Strhodes
2211135446Strhodes	if (eresult != ISC_R_SUCCESS) {
2212135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2213135446Strhodes
2214135446Strhodes		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2215135446Strhodes		fprintf(stderr, "; Communication with %s failed: %s\n",
2216193149Sdougb			addrbuf, isc_result_totext(eresult));
2217135446Strhodes		if (userserver != NULL)
2218135446Strhodes			fatal("could not talk to specified name server");
2219135446Strhodes		else if (++ns_inuse >= lwconf->nsnext)
2220135446Strhodes			fatal("could not talk to any default name server");
2221135446Strhodes		ddebug("Destroying request [%p]", request);
2222135446Strhodes		dns_request_destroy(&request);
2223135446Strhodes		dns_message_renderreset(soaquery);
2224153816Sdougb		dns_message_settsigkey(soaquery, NULL);
2225135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2226135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2227135446Strhodes		isc_event_free(&event);
2228135446Strhodes		setzoneclass(dns_rdataclass_none);
2229135446Strhodes		return;
2230135446Strhodes	}
2231170222Sdougb
2232135446Strhodes	isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2233170222Sdougb	reqinfo = NULL;
2234135446Strhodes	isc_event_free(&event);
2235135446Strhodes	reqev = NULL;
2236135446Strhodes
2237135446Strhodes	ddebug("About to create rcvmsg");
2238135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2239135446Strhodes	check_result(result, "dns_message_create");
2240135446Strhodes	result = dns_request_getresponse(request, rcvmsg,
2241135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
2242135446Strhodes	if (result == DNS_R_TSIGERRORSET && userserver != NULL) {
2243135446Strhodes		dns_message_destroy(&rcvmsg);
2244135446Strhodes		ddebug("Destroying request [%p]", request);
2245135446Strhodes		dns_request_destroy(&request);
2246135446Strhodes		reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2247135446Strhodes		if (reqinfo == NULL)
2248135446Strhodes			fatal("out of memory");
2249135446Strhodes		reqinfo->msg = soaquery;
2250135446Strhodes		reqinfo->addr = addr;
2251135446Strhodes		dns_message_renderreset(soaquery);
2252135446Strhodes		ddebug("retrying soa request without TSIG");
2253135446Strhodes		result = dns_request_createvia3(requestmgr, soaquery,
2254135446Strhodes						localaddr, addr, 0, NULL,
2255135446Strhodes						FIND_TIMEOUT * 20,
2256165071Sdougb						FIND_TIMEOUT, 3,
2257135446Strhodes						global_task, recvsoa, reqinfo,
2258135446Strhodes						&request);
2259135446Strhodes		check_result(result, "dns_request_createvia");
2260135446Strhodes		requests++;
2261135446Strhodes		return;
2262135446Strhodes	}
2263135446Strhodes	check_result(result, "dns_request_getresponse");
2264135446Strhodes	section = DNS_SECTION_ANSWER;
2265225361Sdougb	POST(section);
2266193149Sdougb	if (debugging)
2267193149Sdougb		show_message(stderr, rcvmsg, "Reply from SOA query:");
2268135446Strhodes
2269135446Strhodes	if (rcvmsg->rcode != dns_rcode_noerror &&
2270135446Strhodes	    rcvmsg->rcode != dns_rcode_nxdomain)
2271135446Strhodes		fatal("response to SOA query was unsuccessful");
2272135446Strhodes
2273170222Sdougb	if (userzone != NULL && rcvmsg->rcode == dns_rcode_nxdomain) {
2274170222Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
2275170222Sdougb		dns_name_format(userzone, namebuf, sizeof(namebuf));
2276170222Sdougb		error("specified zone '%s' does not exist (NXDOMAIN)",
2277170222Sdougb		      namebuf);
2278170222Sdougb		dns_message_destroy(&rcvmsg);
2279170222Sdougb		dns_request_destroy(&request);
2280170222Sdougb		dns_message_destroy(&soaquery);
2281170222Sdougb		ddebug("Out of recvsoa");
2282170222Sdougb		done_update();
2283170222Sdougb		return;
2284170222Sdougb	}
2285170222Sdougb
2286135446Strhodes lookforsoa:
2287135446Strhodes	if (pass == 0)
2288135446Strhodes		section = DNS_SECTION_ANSWER;
2289135446Strhodes	else if (pass == 1)
2290135446Strhodes		section = DNS_SECTION_AUTHORITY;
2291186462Sdougb	else
2292143731Sdougb		goto droplabel;
2293135446Strhodes
2294135446Strhodes	result = dns_message_firstname(rcvmsg, section);
2295135446Strhodes	if (result != ISC_R_SUCCESS) {
2296135446Strhodes		pass++;
2297135446Strhodes		goto lookforsoa;
2298135446Strhodes	}
2299135446Strhodes	while (result == ISC_R_SUCCESS) {
2300135446Strhodes		name = NULL;
2301135446Strhodes		dns_message_currentname(rcvmsg, section, &name);
2302135446Strhodes		soaset = NULL;
2303135446Strhodes		result = dns_message_findtype(name, dns_rdatatype_soa, 0,
2304135446Strhodes					      &soaset);
2305135446Strhodes		if (result == ISC_R_SUCCESS)
2306135446Strhodes			break;
2307135446Strhodes		if (section == DNS_SECTION_ANSWER) {
2308135446Strhodes			dns_rdataset_t *tset = NULL;
2309135446Strhodes			if (dns_message_findtype(name, dns_rdatatype_cname, 0,
2310193149Sdougb						 &tset) == ISC_R_SUCCESS ||
2311135446Strhodes			    dns_message_findtype(name, dns_rdatatype_dname, 0,
2312193149Sdougb						 &tset) == ISC_R_SUCCESS ) {
2313135446Strhodes				seencname = ISC_TRUE;
2314135446Strhodes				break;
2315135446Strhodes			}
2316135446Strhodes		}
2317186462Sdougb
2318135446Strhodes		result = dns_message_nextname(rcvmsg, section);
2319135446Strhodes	}
2320135446Strhodes
2321135446Strhodes	if (soaset == NULL && !seencname) {
2322135446Strhodes		pass++;
2323135446Strhodes		goto lookforsoa;
2324135446Strhodes	}
2325135446Strhodes
2326143731Sdougb	if (seencname)
2327143731Sdougb		goto droplabel;
2328135446Strhodes
2329135446Strhodes	if (debugging) {
2330135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2331135446Strhodes		dns_name_format(name, namestr, sizeof(namestr));
2332135446Strhodes		fprintf(stderr, "Found zone name: %s\n", namestr);
2333135446Strhodes	}
2334135446Strhodes
2335135446Strhodes	result = dns_rdataset_first(soaset);
2336135446Strhodes	check_result(result, "dns_rdataset_first");
2337135446Strhodes
2338135446Strhodes	dns_rdata_init(&soarr);
2339135446Strhodes	dns_rdataset_current(soaset, &soarr);
2340135446Strhodes	result = dns_rdata_tostruct(&soarr, &soa, NULL);
2341135446Strhodes	check_result(result, "dns_rdata_tostruct");
2342135446Strhodes
2343135446Strhodes	dns_name_init(&master, NULL);
2344135446Strhodes	dns_name_clone(&soa.origin, &master);
2345135446Strhodes
2346135446Strhodes	if (userzone != NULL)
2347135446Strhodes		zonename = userzone;
2348135446Strhodes	else
2349135446Strhodes		zonename = name;
2350135446Strhodes
2351135446Strhodes	if (debugging) {
2352135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2353135446Strhodes		dns_name_format(&master, namestr, sizeof(namestr));
2354135446Strhodes		fprintf(stderr, "The master is: %s\n", namestr);
2355135446Strhodes	}
2356135446Strhodes
2357135446Strhodes	if (userserver != NULL)
2358135446Strhodes		serveraddr = userserver;
2359135446Strhodes	else {
2360135446Strhodes		char serverstr[DNS_NAME_MAXTEXT+1];
2361135446Strhodes		isc_buffer_t buf;
2362135446Strhodes
2363135446Strhodes		isc_buffer_init(&buf, serverstr, sizeof(serverstr));
2364135446Strhodes		result = dns_name_totext(&master, ISC_TRUE, &buf);
2365135446Strhodes		check_result(result, "dns_name_totext");
2366135446Strhodes		serverstr[isc_buffer_usedlength(&buf)] = 0;
2367224092Sdougb		get_address(serverstr, dnsport, &tempaddr);
2368135446Strhodes		serveraddr = &tempaddr;
2369135446Strhodes	}
2370143731Sdougb	dns_rdata_freestruct(&soa);
2371135446Strhodes
2372193149Sdougb#ifdef GSSAPI
2373193149Sdougb	if (usegsstsig) {
2374193149Sdougb		dns_name_init(&tmpzonename, NULL);
2375193149Sdougb		dns_name_dup(zonename, mctx, &tmpzonename);
2376193149Sdougb		dns_name_init(&restart_master, NULL);
2377193149Sdougb		dns_name_dup(&master, mctx, &restart_master);
2378193149Sdougb		start_gssrequest(&master);
2379193149Sdougb	} else {
2380193149Sdougb		send_update(zonename, serveraddr, localaddr);
2381193149Sdougb		setzoneclass(dns_rdataclass_none);
2382193149Sdougb	}
2383193149Sdougb#else
2384135446Strhodes	send_update(zonename, serveraddr, localaddr);
2385143731Sdougb	setzoneclass(dns_rdataclass_none);
2386193149Sdougb#endif
2387135446Strhodes
2388135446Strhodes	dns_message_destroy(&soaquery);
2389135446Strhodes	dns_request_destroy(&request);
2390135446Strhodes
2391135446Strhodes out:
2392135446Strhodes	dns_message_destroy(&rcvmsg);
2393135446Strhodes	ddebug("Out of recvsoa");
2394143731Sdougb	return;
2395186462Sdougb
2396143731Sdougb droplabel:
2397143731Sdougb	result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
2398143731Sdougb	INSIST(result == ISC_R_SUCCESS);
2399143731Sdougb	name = NULL;
2400143731Sdougb	dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
2401143731Sdougb	nlabels = dns_name_countlabels(name);
2402143731Sdougb	if (nlabels == 1)
2403143731Sdougb		fatal("could not find enclosing zone");
2404143731Sdougb	dns_name_init(&tname, NULL);
2405143731Sdougb	dns_name_getlabelsequence(name, 1, nlabels - 1, &tname);
2406143731Sdougb	dns_name_clone(&tname, name);
2407143731Sdougb	dns_request_destroy(&request);
2408143731Sdougb	dns_message_renderreset(soaquery);
2409153816Sdougb	dns_message_settsigkey(soaquery, NULL);
2410143731Sdougb	if (userserver != NULL)
2411143731Sdougb		sendrequest(localaddr, userserver, soaquery, &request);
2412143731Sdougb	else
2413193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2414143731Sdougb	goto out;
2415135446Strhodes}
2416135446Strhodes
2417135446Strhodesstatic void
2418135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2419135446Strhodes	    dns_message_t *msg, dns_request_t **request)
2420135446Strhodes{
2421135446Strhodes	isc_result_t result;
2422135446Strhodes	nsu_requestinfo_t *reqinfo;
2423135446Strhodes
2424135446Strhodes	reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2425135446Strhodes	if (reqinfo == NULL)
2426135446Strhodes		fatal("out of memory");
2427135446Strhodes	reqinfo->msg = msg;
2428135446Strhodes	reqinfo->addr = destaddr;
2429135446Strhodes	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr, 0,
2430135446Strhodes					(userserver != NULL) ? tsigkey : NULL,
2431135446Strhodes					FIND_TIMEOUT * 20, FIND_TIMEOUT, 3,
2432135446Strhodes					global_task, recvsoa, reqinfo, request);
2433135446Strhodes	check_result(result, "dns_request_createvia");
2434135446Strhodes	requests++;
2435135446Strhodes}
2436135446Strhodes
2437193149Sdougb#ifdef GSSAPI
2438224092Sdougb
2439224092Sdougb/*
2440224092Sdougb * Get the realm from the users kerberos ticket if possible
2441224092Sdougb */
2442135446Strhodesstatic void
2443224092Sdougbget_ticket_realm(isc_mem_t *mctx)
2444193149Sdougb{
2445224092Sdougb	krb5_context ctx;
2446224092Sdougb	krb5_error_code rc;
2447224092Sdougb	krb5_ccache ccache;
2448224092Sdougb	krb5_principal princ;
2449224092Sdougb	char *name, *ticket_realm;
2450224092Sdougb
2451224092Sdougb	rc = krb5_init_context(&ctx);
2452224092Sdougb	if (rc != 0)
2453224092Sdougb		return;
2454224092Sdougb
2455224092Sdougb	rc = krb5_cc_default(ctx, &ccache);
2456224092Sdougb	if (rc != 0) {
2457224092Sdougb		krb5_free_context(ctx);
2458224092Sdougb		return;
2459224092Sdougb	}
2460224092Sdougb
2461224092Sdougb	rc = krb5_cc_get_principal(ctx, ccache, &princ);
2462224092Sdougb	if (rc != 0) {
2463224092Sdougb		krb5_cc_close(ctx, ccache);
2464224092Sdougb		krb5_free_context(ctx);
2465224092Sdougb		return;
2466224092Sdougb	}
2467224092Sdougb
2468224092Sdougb	rc = krb5_unparse_name(ctx, princ, &name);
2469224092Sdougb	if (rc != 0) {
2470224092Sdougb		krb5_free_principal(ctx, princ);
2471224092Sdougb		krb5_cc_close(ctx, ccache);
2472224092Sdougb		krb5_free_context(ctx);
2473224092Sdougb		return;
2474224092Sdougb	}
2475224092Sdougb
2476224092Sdougb	ticket_realm = strrchr(name, '@');
2477224092Sdougb	if (ticket_realm != NULL) {
2478224092Sdougb		realm = isc_mem_strdup(mctx, ticket_realm);
2479224092Sdougb	}
2480224092Sdougb
2481224092Sdougb	free(name);
2482224092Sdougb	krb5_free_principal(ctx, princ);
2483224092Sdougb	krb5_cc_close(ctx, ccache);
2484224092Sdougb	krb5_free_context(ctx);
2485224092Sdougb	if (realm != NULL && debugging)
2486224092Sdougb		fprintf(stderr, "Found realm from ticket: %s\n", realm+1);
2487224092Sdougb}
2488224092Sdougb
2489224092Sdougb
2490224092Sdougbstatic void
2491224092Sdougbstart_gssrequest(dns_name_t *master) {
2492193149Sdougb	gss_ctx_id_t context;
2493193149Sdougb	isc_buffer_t buf;
2494193149Sdougb	isc_result_t result;
2495193149Sdougb	isc_uint32_t val = 0;
2496193149Sdougb	dns_message_t *rmsg;
2497193149Sdougb	dns_request_t *request = NULL;
2498193149Sdougb	dns_name_t *servname;
2499193149Sdougb	dns_fixedname_t fname;
2500193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
2501193149Sdougb	char keystr[DNS_NAME_FORMATSIZE];
2502224092Sdougb	char *err_message = NULL;
2503193149Sdougb
2504193149Sdougb	debug("start_gssrequest");
2505193149Sdougb	usevc = ISC_TRUE;
2506193149Sdougb
2507193149Sdougb	if (gssring != NULL)
2508224092Sdougb		dns_tsigkeyring_detach(&gssring);
2509193149Sdougb	gssring = NULL;
2510193149Sdougb	result = dns_tsigkeyring_create(mctx, &gssring);
2511193149Sdougb
2512193149Sdougb	if (result != ISC_R_SUCCESS)
2513193149Sdougb		fatal("dns_tsigkeyring_create failed: %s",
2514193149Sdougb		      isc_result_totext(result));
2515193149Sdougb
2516193149Sdougb	dns_name_format(master, namestr, sizeof(namestr));
2517193149Sdougb	if (kserver == NULL) {
2518193149Sdougb		kserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
2519193149Sdougb		if (kserver == NULL)
2520193149Sdougb			fatal("out of memory");
2521193149Sdougb	}
2522193149Sdougb	if (userserver == NULL)
2523224092Sdougb		get_address(namestr, dnsport, kserver);
2524193149Sdougb	else
2525193149Sdougb		(void)memcpy(kserver, userserver, sizeof(isc_sockaddr_t));
2526193149Sdougb
2527193149Sdougb	dns_fixedname_init(&fname);
2528193149Sdougb	servname = dns_fixedname_name(&fname);
2529193149Sdougb
2530224092Sdougb	if (realm == NULL)
2531224092Sdougb		get_ticket_realm(mctx);
2532224092Sdougb
2533193149Sdougb	result = isc_string_printf(servicename, sizeof(servicename),
2534218384Sdougb				   "DNS/%s%s", namestr, realm ? realm : "");
2535193149Sdougb	if (result != ISC_R_SUCCESS)
2536193149Sdougb		fatal("isc_string_printf(servicename) failed: %s",
2537193149Sdougb		      isc_result_totext(result));
2538193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2539193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2540224092Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL);
2541193149Sdougb	if (result != ISC_R_SUCCESS)
2542193149Sdougb		fatal("dns_name_fromtext(servname) failed: %s",
2543193149Sdougb		      isc_result_totext(result));
2544193149Sdougb
2545193149Sdougb	dns_fixedname_init(&fkname);
2546193149Sdougb	keyname = dns_fixedname_name(&fkname);
2547193149Sdougb
2548193149Sdougb	isc_random_get(&val);
2549193149Sdougb	result = isc_string_printf(keystr, sizeof(keystr), "%u.sig-%s",
2550193149Sdougb				   val, namestr);
2551193149Sdougb	if (result != ISC_R_SUCCESS)
2552193149Sdougb		fatal("isc_string_printf(keystr) failed: %s",
2553193149Sdougb		      isc_result_totext(result));
2554193149Sdougb	isc_buffer_init(&buf, keystr, strlen(keystr));
2555193149Sdougb	isc_buffer_add(&buf, strlen(keystr));
2556193149Sdougb
2557224092Sdougb	result = dns_name_fromtext(keyname, &buf, dns_rootname, 0, NULL);
2558193149Sdougb	if (result != ISC_R_SUCCESS)
2559193149Sdougb		fatal("dns_name_fromtext(keyname) failed: %s",
2560193149Sdougb		      isc_result_totext(result));
2561193149Sdougb
2562193149Sdougb	/* Windows doesn't recognize name compression in the key name. */
2563193149Sdougb	keyname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2564193149Sdougb
2565193149Sdougb	rmsg = NULL;
2566193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &rmsg);
2567193149Sdougb	if (result != ISC_R_SUCCESS)
2568193149Sdougb		fatal("dns_message_create failed: %s",
2569193149Sdougb		      isc_result_totext(result));
2570193149Sdougb
2571193149Sdougb	/* Build first request. */
2572193149Sdougb	context = GSS_C_NO_CONTEXT;
2573193149Sdougb	result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0,
2574224092Sdougb					&context, use_win2k_gsstsig,
2575224092Sdougb					mctx, &err_message);
2576193149Sdougb	if (result == ISC_R_FAILURE)
2577224092Sdougb		fatal("tkey query failed: %s",
2578224092Sdougb		      err_message != NULL ? err_message : "unknown error");
2579193149Sdougb	if (result != ISC_R_SUCCESS)
2580193149Sdougb		fatal("dns_tkey_buildgssquery failed: %s",
2581193149Sdougb		      isc_result_totext(result));
2582193149Sdougb
2583193149Sdougb	send_gssrequest(localaddr, kserver, rmsg, &request, context);
2584193149Sdougb}
2585193149Sdougb
2586193149Sdougbstatic void
2587193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2588193149Sdougb		dns_message_t *msg, dns_request_t **request,
2589193149Sdougb		gss_ctx_id_t context)
2590193149Sdougb{
2591193149Sdougb	isc_result_t result;
2592193149Sdougb	nsu_gssinfo_t *reqinfo;
2593193149Sdougb	unsigned int options = 0;
2594193149Sdougb
2595193149Sdougb	debug("send_gssrequest");
2596193149Sdougb	reqinfo = isc_mem_get(mctx, sizeof(nsu_gssinfo_t));
2597193149Sdougb	if (reqinfo == NULL)
2598193149Sdougb		fatal("out of memory");
2599193149Sdougb	reqinfo->msg = msg;
2600193149Sdougb	reqinfo->addr = destaddr;
2601193149Sdougb	reqinfo->context = context;
2602193149Sdougb
2603193149Sdougb	options |= DNS_REQUESTOPT_TCP;
2604193149Sdougb	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr,
2605193149Sdougb					options, tsigkey, FIND_TIMEOUT * 20,
2606193149Sdougb					FIND_TIMEOUT, 3, global_task, recvgss,
2607193149Sdougb					reqinfo, request);
2608193149Sdougb	check_result(result, "dns_request_createvia3");
2609193149Sdougb	if (debugging)
2610193149Sdougb		show_message(stdout, msg, "Outgoing update query:");
2611193149Sdougb	requests++;
2612193149Sdougb}
2613193149Sdougb
2614193149Sdougbstatic void
2615193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event) {
2616193149Sdougb	dns_requestevent_t *reqev = NULL;
2617193149Sdougb	dns_request_t *request = NULL;
2618193149Sdougb	isc_result_t result, eresult;
2619193149Sdougb	dns_message_t *rcvmsg = NULL;
2620193149Sdougb	nsu_gssinfo_t *reqinfo;
2621193149Sdougb	dns_message_t *tsigquery = NULL;
2622193149Sdougb	isc_sockaddr_t *addr;
2623193149Sdougb	gss_ctx_id_t context;
2624193149Sdougb	isc_buffer_t buf;
2625193149Sdougb	dns_name_t *servname;
2626193149Sdougb	dns_fixedname_t fname;
2627224092Sdougb	char *err_message = NULL;
2628193149Sdougb
2629193149Sdougb	UNUSED(task);
2630193149Sdougb
2631193149Sdougb	ddebug("recvgss()");
2632193149Sdougb
2633193149Sdougb	requests--;
2634193149Sdougb
2635193149Sdougb	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2636193149Sdougb	reqev = (dns_requestevent_t *)event;
2637193149Sdougb	request = reqev->request;
2638193149Sdougb	eresult = reqev->result;
2639193149Sdougb	reqinfo = reqev->ev_arg;
2640193149Sdougb	tsigquery = reqinfo->msg;
2641193149Sdougb	context = reqinfo->context;
2642193149Sdougb	addr = reqinfo->addr;
2643193149Sdougb
2644193149Sdougb	if (shuttingdown) {
2645193149Sdougb		dns_request_destroy(&request);
2646193149Sdougb		dns_message_destroy(&tsigquery);
2647193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2648193149Sdougb		isc_event_free(&event);
2649193149Sdougb		maybeshutdown();
2650193149Sdougb		return;
2651193149Sdougb	}
2652193149Sdougb
2653193149Sdougb	if (eresult != ISC_R_SUCCESS) {
2654193149Sdougb		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2655193149Sdougb
2656193149Sdougb		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2657193149Sdougb		fprintf(stderr, "; Communication with %s failed: %s\n",
2658193149Sdougb			addrbuf, isc_result_totext(eresult));
2659193149Sdougb		if (userserver != NULL)
2660193149Sdougb			fatal("could not talk to specified name server");
2661193149Sdougb		else if (++ns_inuse >= lwconf->nsnext)
2662193149Sdougb			fatal("could not talk to any default name server");
2663193149Sdougb		ddebug("Destroying request [%p]", request);
2664193149Sdougb		dns_request_destroy(&request);
2665193149Sdougb		dns_message_renderreset(tsigquery);
2666193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], tsigquery,
2667193149Sdougb			    &request);
2668193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2669193149Sdougb		isc_event_free(&event);
2670193149Sdougb		return;
2671193149Sdougb	}
2672193149Sdougb	isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2673193149Sdougb
2674193149Sdougb	isc_event_free(&event);
2675193149Sdougb	reqev = NULL;
2676193149Sdougb
2677193149Sdougb	ddebug("recvgss creating rcvmsg");
2678193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2679193149Sdougb	check_result(result, "dns_message_create");
2680193149Sdougb
2681193149Sdougb	result = dns_request_getresponse(request, rcvmsg,
2682193149Sdougb					 DNS_MESSAGEPARSE_PRESERVEORDER);
2683193149Sdougb	check_result(result, "dns_request_getresponse");
2684193149Sdougb
2685193149Sdougb	if (debugging)
2686193149Sdougb		show_message(stderr, rcvmsg,
2687193149Sdougb			     "recvmsg reply from GSS-TSIG query");
2688193149Sdougb
2689193149Sdougb	if (rcvmsg->rcode == dns_rcode_formerr && !tried_other_gsstsig) {
2690193149Sdougb		ddebug("recvgss trying %s GSS-TSIG",
2691193149Sdougb		       use_win2k_gsstsig ? "Standard" : "Win2k");
2692193149Sdougb		if (use_win2k_gsstsig)
2693193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
2694193149Sdougb		else
2695193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
2696193149Sdougb		tried_other_gsstsig = ISC_TRUE;
2697193149Sdougb		start_gssrequest(&restart_master);
2698193149Sdougb		goto done;
2699193149Sdougb	}
2700193149Sdougb
2701193149Sdougb	if (rcvmsg->rcode != dns_rcode_noerror &&
2702193149Sdougb	    rcvmsg->rcode != dns_rcode_nxdomain)
2703193149Sdougb		fatal("response to GSS-TSIG query was unsuccessful");
2704193149Sdougb
2705193149Sdougb
2706193149Sdougb	dns_fixedname_init(&fname);
2707193149Sdougb	servname = dns_fixedname_name(&fname);
2708193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2709193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2710224092Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL);
2711193149Sdougb	check_result(result, "dns_name_fromtext");
2712193149Sdougb
2713193149Sdougb	tsigkey = NULL;
2714193149Sdougb	result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname,
2715193149Sdougb				       &context, &tsigkey, gssring,
2716224092Sdougb				       use_win2k_gsstsig,
2717224092Sdougb				       &err_message);
2718193149Sdougb	switch (result) {
2719193149Sdougb
2720193149Sdougb	case DNS_R_CONTINUE:
2721193149Sdougb		send_gssrequest(localaddr, kserver, tsigquery, &request,
2722193149Sdougb				context);
2723193149Sdougb		break;
2724193149Sdougb
2725193149Sdougb	case ISC_R_SUCCESS:
2726193149Sdougb		/*
2727193149Sdougb		 * XXXSRA Waaay too much fun here.  There's no good
2728193149Sdougb		 * reason why we need a TSIG here (the people who put
2729193149Sdougb		 * it into the spec admitted at the time that it was
2730193149Sdougb		 * not a security issue), and Windows clients don't
2731193149Sdougb		 * seem to work if named complies with the spec and
2732193149Sdougb		 * includes the gratuitous TSIG.  So we're in the
2733193149Sdougb		 * bizarre situation of having to choose between
2734193149Sdougb		 * complying with a useless requirement in the spec
2735193149Sdougb		 * and interoperating.  This is nuts.  If we can
2736193149Sdougb		 * confirm this behavior, we should ask the WG to
2737193149Sdougb		 * consider removing the requirement for the
2738193149Sdougb		 * gratuitous TSIG here.  For the moment, we ignore
2739193149Sdougb		 * the TSIG -- this too is a spec violation, but it's
2740193149Sdougb		 * the least insane thing to do.
2741193149Sdougb		 */
2742193149Sdougb#if 0
2743193149Sdougb		/*
2744193149Sdougb		 * Verify the signature.
2745193149Sdougb		 */
2746193149Sdougb		rcvmsg->state = DNS_SECTION_ANY;
2747193149Sdougb		dns_message_setquerytsig(rcvmsg, NULL);
2748193149Sdougb		result = dns_message_settsigkey(rcvmsg, tsigkey);
2749193149Sdougb		check_result(result, "dns_message_settsigkey");
2750193149Sdougb		result = dns_message_checksig(rcvmsg, NULL);
2751193149Sdougb		ddebug("tsig verification: %s", dns_result_totext(result));
2752193149Sdougb		check_result(result, "dns_message_checksig");
2753193149Sdougb#endif /* 0 */
2754193149Sdougb
2755193149Sdougb		send_update(&tmpzonename, serveraddr, localaddr);
2756193149Sdougb		setzoneclass(dns_rdataclass_none);
2757193149Sdougb		break;
2758193149Sdougb
2759193149Sdougb	default:
2760224092Sdougb		fatal("dns_tkey_negotiategss: %s %s",
2761224092Sdougb		      isc_result_totext(result),
2762224092Sdougb		      err_message != NULL ? err_message : "");
2763193149Sdougb	}
2764193149Sdougb
2765193149Sdougb done:
2766193149Sdougb	dns_request_destroy(&request);
2767193149Sdougb	dns_message_destroy(&tsigquery);
2768193149Sdougb
2769193149Sdougb	dns_message_destroy(&rcvmsg);
2770193149Sdougb	ddebug("Out of recvgss");
2771193149Sdougb}
2772193149Sdougb#endif
2773193149Sdougb
2774193149Sdougbstatic void
2775135446Strhodesstart_update(void) {
2776135446Strhodes	isc_result_t result;
2777135446Strhodes	dns_rdataset_t *rdataset = NULL;
2778135446Strhodes	dns_name_t *name = NULL;
2779135446Strhodes	dns_request_t *request = NULL;
2780135446Strhodes	dns_message_t *soaquery = NULL;
2781135446Strhodes	dns_name_t *firstname;
2782135446Strhodes	dns_section_t section = DNS_SECTION_UPDATE;
2783135446Strhodes
2784135446Strhodes	ddebug("start_update()");
2785135446Strhodes
2786135446Strhodes	if (answer != NULL)
2787135446Strhodes		dns_message_destroy(&answer);
2788135446Strhodes
2789193149Sdougb	if (userzone != NULL && userserver != NULL && ! usegsstsig) {
2790135446Strhodes		send_update(userzone, userserver, localaddr);
2791135446Strhodes		setzoneclass(dns_rdataclass_none);
2792135446Strhodes		return;
2793135446Strhodes	}
2794135446Strhodes
2795135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
2796135446Strhodes				    &soaquery);
2797135446Strhodes	check_result(result, "dns_message_create");
2798135446Strhodes
2799170222Sdougb	if (userserver == NULL)
2800170222Sdougb		soaquery->flags |= DNS_MESSAGEFLAG_RD;
2801135446Strhodes
2802135446Strhodes	result = dns_message_gettempname(soaquery, &name);
2803135446Strhodes	check_result(result, "dns_message_gettempname");
2804135446Strhodes
2805135446Strhodes	result = dns_message_gettemprdataset(soaquery, &rdataset);
2806135446Strhodes	check_result(result, "dns_message_gettemprdataset");
2807135446Strhodes
2808135446Strhodes	dns_rdataset_makequestion(rdataset, getzoneclass(), dns_rdatatype_soa);
2809135446Strhodes
2810170222Sdougb	if (userzone != NULL) {
2811170222Sdougb		dns_name_init(name, NULL);
2812170222Sdougb		dns_name_clone(userzone, name);
2813170222Sdougb	} else {
2814218384Sdougb		dns_rdataset_t *tmprdataset;
2815170222Sdougb		result = dns_message_firstname(updatemsg, section);
2816170222Sdougb		if (result == ISC_R_NOMORE) {
2817170222Sdougb			section = DNS_SECTION_PREREQUISITE;
2818170222Sdougb			result = dns_message_firstname(updatemsg, section);
2819170222Sdougb		}
2820170222Sdougb		if (result != ISC_R_SUCCESS) {
2821174187Sdougb			dns_message_puttempname(soaquery, &name);
2822174187Sdougb			dns_rdataset_disassociate(rdataset);
2823174187Sdougb			dns_message_puttemprdataset(soaquery, &rdataset);
2824174187Sdougb			dns_message_destroy(&soaquery);
2825170222Sdougb			done_update();
2826170222Sdougb			return;
2827170222Sdougb		}
2828170222Sdougb		firstname = NULL;
2829170222Sdougb		dns_message_currentname(updatemsg, section, &firstname);
2830170222Sdougb		dns_name_init(name, NULL);
2831170222Sdougb		dns_name_clone(firstname, name);
2832218384Sdougb		/*
2833218384Sdougb		 * Looks to see if the first name references a DS record
2834218384Sdougb		 * and if that name is not the root remove a label as DS
2835218384Sdougb		 * records live in the parent zone so we need to start our
2836218384Sdougb		 * search one label up.
2837218384Sdougb		 */
2838218384Sdougb		tmprdataset = ISC_LIST_HEAD(firstname->list);
2839218384Sdougb		if (section == DNS_SECTION_UPDATE &&
2840218384Sdougb		    !dns_name_equal(firstname, dns_rootname) &&
2841218384Sdougb		    tmprdataset->type == dns_rdatatype_ds) {
2842218384Sdougb		    unsigned int labels = dns_name_countlabels(name);
2843218384Sdougb		    dns_name_getlabelsequence(name, 1, labels - 1, name);
2844218384Sdougb		}
2845170222Sdougb	}
2846135446Strhodes
2847135446Strhodes	ISC_LIST_INIT(name->list);
2848135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
2849135446Strhodes	dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
2850135446Strhodes
2851135446Strhodes	if (userserver != NULL)
2852135446Strhodes		sendrequest(localaddr, userserver, soaquery, &request);
2853135446Strhodes	else {
2854135446Strhodes		ns_inuse = 0;
2855135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2856135446Strhodes	}
2857135446Strhodes}
2858135446Strhodes
2859135446Strhodesstatic void
2860135446Strhodescleanup(void) {
2861135446Strhodes	ddebug("cleanup()");
2862135446Strhodes
2863135446Strhodes	if (answer != NULL)
2864135446Strhodes		dns_message_destroy(&answer);
2865193149Sdougb
2866193149Sdougb#ifdef GSSAPI
2867193149Sdougb	if (tsigkey != NULL) {
2868193149Sdougb		ddebug("detach tsigkey x%p", tsigkey);
2869193149Sdougb		dns_tsigkey_detach(&tsigkey);
2870193149Sdougb	}
2871193149Sdougb	if (gssring != NULL) {
2872224092Sdougb		ddebug("Detaching GSS-TSIG keyring");
2873224092Sdougb		dns_tsigkeyring_detach(&gssring);
2874193149Sdougb	}
2875193149Sdougb	if (kserver != NULL) {
2876193149Sdougb		isc_mem_put(mctx, kserver, sizeof(isc_sockaddr_t));
2877193149Sdougb		kserver = NULL;
2878193149Sdougb	}
2879218384Sdougb	if (realm != NULL) {
2880218384Sdougb		isc_mem_free(mctx, realm);
2881218384Sdougb		realm = NULL;
2882218384Sdougb	}
2883193149Sdougb#endif
2884193149Sdougb
2885225361Sdougb	if (sig0key != NULL)
2886225361Sdougb		dst_key_free(&sig0key);
2887225361Sdougb
2888135446Strhodes	ddebug("Shutting down task manager");
2889135446Strhodes	isc_taskmgr_destroy(&taskmgr);
2890135446Strhodes
2891135446Strhodes	ddebug("Destroying event");
2892135446Strhodes	isc_event_free(&global_event);
2893135446Strhodes
2894135446Strhodes	ddebug("Shutting down socket manager");
2895135446Strhodes	isc_socketmgr_destroy(&socketmgr);
2896135446Strhodes
2897135446Strhodes	ddebug("Shutting down timer manager");
2898135446Strhodes	isc_timermgr_destroy(&timermgr);
2899135446Strhodes
2900135446Strhodes	ddebug("Destroying hash context");
2901135446Strhodes	isc_hash_destroy();
2902135446Strhodes
2903170222Sdougb	ddebug("Destroying name state");
2904170222Sdougb	dns_name_destroy();
2905170222Sdougb
2906193149Sdougb	ddebug("Removing log context");
2907193149Sdougb	isc_log_destroy(&lctx);
2908193149Sdougb
2909135446Strhodes	ddebug("Destroying memory context");
2910135446Strhodes	if (memdebugging)
2911135446Strhodes		isc_mem_stats(mctx, stderr);
2912135446Strhodes	isc_mem_destroy(&mctx);
2913135446Strhodes}
2914135446Strhodes
2915135446Strhodesstatic void
2916135446Strhodesgetinput(isc_task_t *task, isc_event_t *event) {
2917135446Strhodes	isc_boolean_t more;
2918135446Strhodes
2919135446Strhodes	UNUSED(task);
2920135446Strhodes
2921135446Strhodes	if (shuttingdown) {
2922135446Strhodes		maybeshutdown();
2923135446Strhodes		return;
2924135446Strhodes	}
2925135446Strhodes
2926135446Strhodes	if (global_event == NULL)
2927135446Strhodes		global_event = event;
2928135446Strhodes
2929135446Strhodes	reset_system();
2930135446Strhodes	more = user_interaction();
2931135446Strhodes	if (!more) {
2932135446Strhodes		isc_app_shutdown();
2933135446Strhodes		return;
2934135446Strhodes	}
2935135446Strhodes	start_update();
2936135446Strhodes	return;
2937135446Strhodes}
2938135446Strhodes
2939135446Strhodesint
2940135446Strhodesmain(int argc, char **argv) {
2941135446Strhodes	isc_result_t result;
2942135446Strhodes	style = &dns_master_style_debug;
2943135446Strhodes
2944135446Strhodes	input = stdin;
2945135446Strhodes
2946135446Strhodes	interactive = ISC_TF(isatty(0));
2947135446Strhodes
2948135446Strhodes	isc_app_start();
2949135446Strhodes
2950193149Sdougb	pre_parse_args(argc, argv);
2951135446Strhodes
2952193149Sdougb	result = isc_mem_create(0, 0, &mctx);
2953193149Sdougb	check_result(result, "isc_mem_create");
2954193149Sdougb
2955193149Sdougb	parse_args(argc, argv, mctx, &entropy);
2956193149Sdougb
2957135446Strhodes	setup_system();
2958135446Strhodes
2959135446Strhodes	result = isc_app_onrun(mctx, global_task, getinput, NULL);
2960135446Strhodes	check_result(result, "isc_app_onrun");
2961135446Strhodes
2962135446Strhodes	(void)isc_app_run();
2963135446Strhodes
2964135446Strhodes	cleanup();
2965135446Strhodes
2966135446Strhodes	isc_app_finish();
2967135446Strhodes
2968135446Strhodes	if (seenerror)
2969135446Strhodes		return (2);
2970135446Strhodes	else
2971135446Strhodes		return (0);
2972135446Strhodes}
2973