1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  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
18254897Serwin/* $Id$ */
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>
84262706Serwin#ifdef WIN32
85262706Serwin#include <krb5/krb5.h>
86262706Serwin#else
87224092Sdougb#include ISC_PLATFORM_KRB5HEADER
88193149Sdougb#endif
89262706Serwin#endif
90135446Strhodes#include <bind9/getaddresses.h>
91135446Strhodes
92254897Serwin#if defined(HAVE_READLINE)
93254897Serwin#include <readline/readline.h>
94254897Serwin#include <readline/history.h>
95254897Serwin#endif
96193149Sdougb
97135446Strhodes#ifdef HAVE_ADDRINFO
98135446Strhodes#ifdef HAVE_GETADDRINFO
99135446Strhodes#ifdef HAVE_GAISTRERROR
100135446Strhodes#define USE_GETADDRINFO
101135446Strhodes#endif
102135446Strhodes#endif
103135446Strhodes#endif
104135446Strhodes
105135446Strhodes#ifndef USE_GETADDRINFO
106135446Strhodes#ifndef ISC_PLATFORM_NONSTDHERRNO
107135446Strhodesextern int h_errno;
108135446Strhodes#endif
109135446Strhodes#endif
110135446Strhodes
111135446Strhodes#define MAXCMD (4 * 1024)
112135446Strhodes#define MAXWIRE (64 * 1024)
113135446Strhodes#define PACKETSIZE ((64 * 1024) - 1)
114135446Strhodes#define INITTEXT (2 * 1024)
115135446Strhodes#define MAXTEXT (128 * 1024)
116135446Strhodes#define FIND_TIMEOUT 5
117135446Strhodes#define TTL_MAX 2147483647U	/* Maximum signed 32 bit integer. */
118135446Strhodes
119135446Strhodes#define DNSDEFAULTPORT 53
120135446Strhodes
121224092Sdougbstatic isc_uint16_t dnsport = DNSDEFAULTPORT;
122224092Sdougb
123135446Strhodes#ifndef RESOLV_CONF
124135446Strhodes#define RESOLV_CONF "/etc/resolv.conf"
125135446Strhodes#endif
126135446Strhodes
127135446Strhodesstatic isc_boolean_t debugging = ISC_FALSE, ddebugging = ISC_FALSE;
128135446Strhodesstatic isc_boolean_t memdebugging = ISC_FALSE;
129135446Strhodesstatic isc_boolean_t have_ipv4 = ISC_FALSE;
130135446Strhodesstatic isc_boolean_t have_ipv6 = ISC_FALSE;
131135446Strhodesstatic isc_boolean_t is_dst_up = ISC_FALSE;
132135446Strhodesstatic isc_boolean_t usevc = ISC_FALSE;
133193149Sdougbstatic isc_boolean_t usegsstsig = ISC_FALSE;
134193149Sdougbstatic isc_boolean_t use_win2k_gsstsig = ISC_FALSE;
135193149Sdougbstatic isc_boolean_t tried_other_gsstsig = ISC_FALSE;
136224092Sdougbstatic isc_boolean_t local_only = ISC_FALSE;
137135446Strhodesstatic isc_taskmgr_t *taskmgr = NULL;
138135446Strhodesstatic isc_task_t *global_task = NULL;
139135446Strhodesstatic isc_event_t *global_event = NULL;
140193149Sdougbstatic isc_log_t *lctx = NULL;
141135446Strhodesstatic isc_mem_t *mctx = NULL;
142135446Strhodesstatic dns_dispatchmgr_t *dispatchmgr = NULL;
143135446Strhodesstatic dns_requestmgr_t *requestmgr = NULL;
144135446Strhodesstatic isc_socketmgr_t *socketmgr = NULL;
145135446Strhodesstatic isc_timermgr_t *timermgr = NULL;
146135446Strhodesstatic dns_dispatch_t *dispatchv4 = NULL;
147135446Strhodesstatic dns_dispatch_t *dispatchv6 = NULL;
148135446Strhodesstatic dns_message_t *updatemsg = NULL;
149135446Strhodesstatic dns_fixedname_t fuserzone;
150135446Strhodesstatic dns_name_t *userzone = NULL;
151193149Sdougbstatic dns_name_t *zonename = NULL;
152193149Sdougbstatic dns_name_t tmpzonename;
153193149Sdougbstatic dns_name_t restart_master;
154193149Sdougbstatic dns_tsig_keyring_t *gssring = NULL;
155135446Strhodesstatic dns_tsigkey_t *tsigkey = NULL;
156225361Sdougbstatic dst_key_t *sig0key = NULL;
157135446Strhodesstatic lwres_context_t *lwctx = NULL;
158135446Strhodesstatic lwres_conf_t *lwconf;
159135446Strhodesstatic isc_sockaddr_t *servers;
160135446Strhodesstatic int ns_inuse = 0;
161135446Strhodesstatic int ns_total = 0;
162135446Strhodesstatic isc_sockaddr_t *userserver = NULL;
163135446Strhodesstatic isc_sockaddr_t *localaddr = NULL;
164193149Sdougbstatic isc_sockaddr_t *serveraddr = NULL;
165193149Sdougbstatic isc_sockaddr_t tempaddr;
166224092Sdougbstatic const char *keyfile = NULL;
167224092Sdougbstatic char *keystr = NULL;
168193149Sdougbstatic isc_entropy_t *entropy = NULL;
169135446Strhodesstatic isc_boolean_t shuttingdown = ISC_FALSE;
170135446Strhodesstatic FILE *input;
171135446Strhodesstatic isc_boolean_t interactive = ISC_TRUE;
172135446Strhodesstatic isc_boolean_t seenerror = ISC_FALSE;
173135446Strhodesstatic const dns_master_style_t *style;
174135446Strhodesstatic int requests = 0;
175193149Sdougbstatic unsigned int logdebuglevel = 0;
176135446Strhodesstatic unsigned int timeout = 300;
177135446Strhodesstatic unsigned int udp_timeout = 3;
178135446Strhodesstatic unsigned int udp_retries = 3;
179135446Strhodesstatic dns_rdataclass_t defaultclass = dns_rdataclass_in;
180135446Strhodesstatic dns_rdataclass_t zoneclass = dns_rdataclass_none;
181135446Strhodesstatic dns_message_t *answer = NULL;
182193149Sdougbstatic isc_uint32_t default_ttl = 0;
183193149Sdougbstatic isc_boolean_t default_ttl_set = ISC_FALSE;
184135446Strhodes
185135446Strhodestypedef struct nsu_requestinfo {
186135446Strhodes	dns_message_t *msg;
187135446Strhodes	isc_sockaddr_t *addr;
188135446Strhodes} nsu_requestinfo_t;
189135446Strhodes
190135446Strhodesstatic void
191135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
192135446Strhodes	    dns_message_t *msg, dns_request_t **request);
193135446Strhodes
194224092SdougbISC_PLATFORM_NORETURN_PRE static void
195224092Sdougbfatal(const char *format, ...)
196224092SdougbISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST;
197224092Sdougb
198135446Strhodesstatic void
199135446Strhodesdebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
200135446Strhodes
201135446Strhodesstatic void
202135446Strhodesddebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
203135446Strhodes
204193149Sdougb#ifdef GSSAPI
205193149Sdougbstatic dns_fixedname_t fkname;
206193149Sdougbstatic isc_sockaddr_t *kserver = NULL;
207218384Sdougbstatic char *realm = NULL;
208193149Sdougbstatic char servicename[DNS_NAME_FORMATSIZE];
209193149Sdougbstatic dns_name_t *keyname;
210193149Sdougbtypedef struct nsu_gssinfo {
211193149Sdougb	dns_message_t *msg;
212193149Sdougb	isc_sockaddr_t *addr;
213193149Sdougb	gss_ctx_id_t context;
214193149Sdougb} nsu_gssinfo_t;
215193149Sdougb
216170222Sdougbstatic void
217193149Sdougbstart_gssrequest(dns_name_t *master);
218193149Sdougbstatic void
219193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
220193149Sdougb		dns_message_t *msg, dns_request_t **request,
221193149Sdougb		gss_ctx_id_t context);
222193149Sdougbstatic void
223193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event);
224193149Sdougb#endif /* GSSAPI */
225193149Sdougb
226193149Sdougbstatic void
227170222Sdougberror(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
228170222Sdougb
229135446Strhodes#define STATUS_MORE	(isc_uint16_t)0
230135446Strhodes#define STATUS_SEND	(isc_uint16_t)1
231135446Strhodes#define STATUS_QUIT	(isc_uint16_t)2
232135446Strhodes#define STATUS_SYNTAX	(isc_uint16_t)3
233135446Strhodes
234193149Sdougbtypedef struct entropysource entropysource_t;
235193149Sdougb
236193149Sdougbstruct entropysource {
237193149Sdougb	isc_entropysource_t *source;
238193149Sdougb	isc_mem_t *mctx;
239193149Sdougb	ISC_LINK(entropysource_t) link;
240193149Sdougb};
241193149Sdougb
242193149Sdougbstatic ISC_LIST(entropysource_t) sources;
243193149Sdougb
244193149Sdougbstatic void
245193149Sdougbsetup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx)
246193149Sdougb{
247193149Sdougb	isc_result_t result;
248193149Sdougb	isc_entropysource_t *source = NULL;
249193149Sdougb	entropysource_t *elt;
250193149Sdougb	int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
251193149Sdougb
252193149Sdougb	REQUIRE(ectx != NULL);
253193149Sdougb
254193149Sdougb	if (*ectx == NULL) {
255193149Sdougb		result = isc_entropy_create(mctx, ectx);
256193149Sdougb		if (result != ISC_R_SUCCESS)
257193149Sdougb			fatal("could not create entropy object");
258193149Sdougb		ISC_LIST_INIT(sources);
259193149Sdougb	}
260193149Sdougb
261193149Sdougb	if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
262193149Sdougb		usekeyboard = ISC_ENTROPY_KEYBOARDYES;
263193149Sdougb		randomfile = NULL;
264193149Sdougb	}
265193149Sdougb
266193149Sdougb	result = isc_entropy_usebestsource(*ectx, &source, randomfile,
267193149Sdougb					   usekeyboard);
268193149Sdougb
269193149Sdougb	if (result != ISC_R_SUCCESS)
270193149Sdougb		fatal("could not initialize entropy source: %s",
271193149Sdougb		      isc_result_totext(result));
272193149Sdougb
273193149Sdougb	if (source != NULL) {
274193149Sdougb		elt = isc_mem_get(mctx, sizeof(*elt));
275193149Sdougb		if (elt == NULL)
276193149Sdougb			fatal("out of memory");
277193149Sdougb		elt->source = source;
278193149Sdougb		elt->mctx = mctx;
279193149Sdougb		ISC_LINK_INIT(elt, link);
280193149Sdougb		ISC_LIST_APPEND(sources, elt, link);
281193149Sdougb	}
282193149Sdougb}
283193149Sdougb
284193149Sdougbstatic void
285193149Sdougbcleanup_entropy(isc_entropy_t **ectx) {
286193149Sdougb	entropysource_t *source;
287193149Sdougb	while (!ISC_LIST_EMPTY(sources)) {
288193149Sdougb		source = ISC_LIST_HEAD(sources);
289193149Sdougb		ISC_LIST_UNLINK(sources, source, link);
290193149Sdougb		isc_entropy_destroysource(&source->source);
291193149Sdougb		isc_mem_put(source->mctx, source, sizeof(*source));
292193149Sdougb	}
293193149Sdougb	isc_entropy_detach(ectx);
294193149Sdougb}
295193149Sdougb
296193149Sdougb
297135446Strhodesstatic dns_rdataclass_t
298135446Strhodesgetzoneclass(void) {
299135446Strhodes	if (zoneclass == dns_rdataclass_none)
300135446Strhodes		zoneclass = defaultclass;
301135446Strhodes	return (zoneclass);
302135446Strhodes}
303135446Strhodes
304135446Strhodesstatic isc_boolean_t
305135446Strhodessetzoneclass(dns_rdataclass_t rdclass) {
306135446Strhodes	if (zoneclass == dns_rdataclass_none ||
307135446Strhodes	    rdclass == dns_rdataclass_none)
308135446Strhodes		zoneclass = rdclass;
309135446Strhodes	if (zoneclass != rdclass)
310135446Strhodes		return (ISC_FALSE);
311135446Strhodes	return (ISC_TRUE);
312135446Strhodes}
313135446Strhodes
314135446Strhodesstatic void
315135446Strhodesfatal(const char *format, ...) {
316135446Strhodes	va_list args;
317135446Strhodes
318135446Strhodes	va_start(args, format);
319135446Strhodes	vfprintf(stderr, format, args);
320135446Strhodes	va_end(args);
321135446Strhodes	fprintf(stderr, "\n");
322135446Strhodes	exit(1);
323135446Strhodes}
324135446Strhodes
325135446Strhodesstatic void
326170222Sdougberror(const char *format, ...) {
327170222Sdougb	va_list args;
328170222Sdougb
329170222Sdougb	va_start(args, format);
330170222Sdougb	vfprintf(stderr, format, args);
331170222Sdougb	va_end(args);
332170222Sdougb	fprintf(stderr, "\n");
333170222Sdougb}
334170222Sdougb
335170222Sdougbstatic void
336135446Strhodesdebug(const char *format, ...) {
337135446Strhodes	va_list args;
338135446Strhodes
339135446Strhodes	if (debugging) {
340135446Strhodes		va_start(args, format);
341135446Strhodes		vfprintf(stderr, format, args);
342135446Strhodes		va_end(args);
343135446Strhodes		fprintf(stderr, "\n");
344135446Strhodes	}
345135446Strhodes}
346135446Strhodes
347135446Strhodesstatic void
348135446Strhodesddebug(const char *format, ...) {
349135446Strhodes	va_list args;
350135446Strhodes
351135446Strhodes	if (ddebugging) {
352135446Strhodes		va_start(args, format);
353135446Strhodes		vfprintf(stderr, format, args);
354135446Strhodes		va_end(args);
355135446Strhodes		fprintf(stderr, "\n");
356135446Strhodes	}
357135446Strhodes}
358135446Strhodes
359135446Strhodesstatic inline void
360135446Strhodescheck_result(isc_result_t result, const char *msg) {
361135446Strhodes	if (result != ISC_R_SUCCESS)
362135446Strhodes		fatal("%s: %s", msg, isc_result_totext(result));
363135446Strhodes}
364135446Strhodes
365135446Strhodesstatic void *
366135446Strhodesmem_alloc(void *arg, size_t size) {
367135446Strhodes	return (isc_mem_get(arg, size));
368135446Strhodes}
369135446Strhodes
370135446Strhodesstatic void
371135446Strhodesmem_free(void *arg, void *mem, size_t size) {
372135446Strhodes	isc_mem_put(arg, mem, size);
373135446Strhodes}
374135446Strhodes
375135446Strhodesstatic char *
376135446Strhodesnsu_strsep(char **stringp, const char *delim) {
377135446Strhodes	char *string = *stringp;
378135446Strhodes	char *s;
379135446Strhodes	const char *d;
380135446Strhodes	char sc, dc;
381135446Strhodes
382135446Strhodes	if (string == NULL)
383135446Strhodes		return (NULL);
384135446Strhodes
385135446Strhodes	for (; *string != '\0'; string++) {
386135446Strhodes		sc = *string;
387135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
388135446Strhodes			if (sc == dc)
389135446Strhodes				break;
390135446Strhodes		}
391135446Strhodes		if (dc == 0)
392135446Strhodes			break;
393135446Strhodes	}
394135446Strhodes
395135446Strhodes	for (s = string; *s != '\0'; s++) {
396135446Strhodes		sc = *s;
397135446Strhodes		for (d = delim; (dc = *d) != '\0'; d++) {
398135446Strhodes			if (sc == dc) {
399135446Strhodes				*s++ = '\0';
400135446Strhodes				*stringp = s;
401135446Strhodes				return (string);
402135446Strhodes			}
403135446Strhodes		}
404135446Strhodes	}
405135446Strhodes	*stringp = NULL;
406135446Strhodes	return (string);
407135446Strhodes}
408135446Strhodes
409135446Strhodesstatic void
410135446Strhodesreset_system(void) {
411135446Strhodes	isc_result_t result;
412135446Strhodes
413135446Strhodes	ddebug("reset_system()");
414135446Strhodes	/* If the update message is still around, destroy it */
415135446Strhodes	if (updatemsg != NULL)
416135446Strhodes		dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER);
417135446Strhodes	else {
418135446Strhodes		result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
419135446Strhodes					    &updatemsg);
420135446Strhodes		check_result(result, "dns_message_create");
421135446Strhodes	}
422135446Strhodes	updatemsg->opcode = dns_opcode_update;
423193149Sdougb	if (usegsstsig) {
424193149Sdougb		if (tsigkey != NULL)
425193149Sdougb			dns_tsigkey_detach(&tsigkey);
426193149Sdougb		if (gssring != NULL)
427224092Sdougb			dns_tsigkeyring_detach(&gssring);
428193149Sdougb		tried_other_gsstsig = ISC_FALSE;
429193149Sdougb	}
430135446Strhodes}
431135446Strhodes
432170222Sdougbstatic isc_uint16_t
433170222Sdougbparse_hmac(dns_name_t **hmac, const char *hmacstr, size_t len) {
434170222Sdougb	isc_uint16_t digestbits = 0;
435170222Sdougb	isc_result_t result;
436170222Sdougb	char buf[20];
437170222Sdougb
438170222Sdougb	REQUIRE(hmac != NULL && *hmac == NULL);
439170222Sdougb	REQUIRE(hmacstr != NULL);
440170222Sdougb
441170222Sdougb	if (len >= sizeof(buf))
442170222Sdougb		fatal("unknown key type '%.*s'", (int)(len), hmacstr);
443170222Sdougb
444170222Sdougb	strncpy(buf, hmacstr, len);
445170222Sdougb	buf[len] = 0;
446186462Sdougb
447170222Sdougb	if (strcasecmp(buf, "hmac-md5") == 0) {
448170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
449170222Sdougb	} else if (strncasecmp(buf, "hmac-md5-", 9) == 0) {
450170222Sdougb		*hmac = DNS_TSIG_HMACMD5_NAME;
451170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[9], 10);
452170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 128)
453170222Sdougb			fatal("digest-bits out of range [0..128]");
454170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
455170222Sdougb	} else if (strcasecmp(buf, "hmac-sha1") == 0) {
456170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
457170222Sdougb	} else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) {
458170222Sdougb		*hmac = DNS_TSIG_HMACSHA1_NAME;
459170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[10], 10);
460170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 160)
461170222Sdougb			fatal("digest-bits out of range [0..160]");
462170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
463170222Sdougb	} else if (strcasecmp(buf, "hmac-sha224") == 0) {
464170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
465170222Sdougb	} else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) {
466170222Sdougb		*hmac = DNS_TSIG_HMACSHA224_NAME;
467170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
468170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 224)
469170222Sdougb			fatal("digest-bits out of range [0..224]");
470170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
471170222Sdougb	} else if (strcasecmp(buf, "hmac-sha256") == 0) {
472170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
473170222Sdougb	} else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) {
474170222Sdougb		*hmac = DNS_TSIG_HMACSHA256_NAME;
475170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
476170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 256)
477170222Sdougb			fatal("digest-bits out of range [0..256]");
478170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
479170222Sdougb	} else if (strcasecmp(buf, "hmac-sha384") == 0) {
480170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
481170222Sdougb	} else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) {
482170222Sdougb		*hmac = DNS_TSIG_HMACSHA384_NAME;
483170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
484170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 384)
485170222Sdougb			fatal("digest-bits out of range [0..384]");
486170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
487170222Sdougb	} else if (strcasecmp(buf, "hmac-sha512") == 0) {
488170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
489170222Sdougb	} else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) {
490170222Sdougb		*hmac = DNS_TSIG_HMACSHA512_NAME;
491170222Sdougb		result = isc_parse_uint16(&digestbits, &buf[12], 10);
492170222Sdougb		if (result != ISC_R_SUCCESS || digestbits > 512)
493170222Sdougb			fatal("digest-bits out of range [0..512]");
494170222Sdougb		digestbits = (digestbits +7) & ~0x7U;
495170222Sdougb	} else
496170222Sdougb		fatal("unknown key type '%s'", buf);
497170222Sdougb	return (digestbits);
498170222Sdougb}
499170222Sdougb
500224092Sdougbstatic int
501224092Sdougbbasenamelen(const char *file) {
502224092Sdougb	int len = strlen(file);
503224092Sdougb
504224092Sdougb	if (len > 1 && file[len - 1] == '.')
505224092Sdougb		len -= 1;
506224092Sdougb	else if (len > 8 && strcmp(file + len - 8, ".private") == 0)
507224092Sdougb		len -= 8;
508224092Sdougb	else if (len > 4 && strcmp(file + len - 4, ".key") == 0)
509224092Sdougb		len -= 4;
510224092Sdougb	return (len);
511224092Sdougb}
512224092Sdougb
513135446Strhodesstatic void
514135446Strhodessetup_keystr(void) {
515135446Strhodes	unsigned char *secret = NULL;
516135446Strhodes	int secretlen;
517135446Strhodes	isc_buffer_t secretbuf;
518135446Strhodes	isc_result_t result;
519135446Strhodes	isc_buffer_t keynamesrc;
520135446Strhodes	char *secretstr;
521170222Sdougb	char *s, *n;
522135446Strhodes	dns_fixedname_t fkeyname;
523135446Strhodes	dns_name_t *keyname;
524170222Sdougb	char *name;
525170222Sdougb	dns_name_t *hmacname = NULL;
526170222Sdougb	isc_uint16_t digestbits = 0;
527135446Strhodes
528135446Strhodes	dns_fixedname_init(&fkeyname);
529135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
530135446Strhodes
531135446Strhodes	debug("Creating key...");
532135446Strhodes
533135446Strhodes	s = strchr(keystr, ':');
534170222Sdougb	if (s == NULL || s == keystr || s[1] == 0)
535170222Sdougb		fatal("key option must specify [hmac:]keyname:secret");
536135446Strhodes	secretstr = s + 1;
537170222Sdougb	n = strchr(secretstr, ':');
538170222Sdougb	if (n != NULL) {
539170222Sdougb		if (n == secretstr || n[1] == 0)
540170222Sdougb			fatal("key option must specify [hmac:]keyname:secret");
541170222Sdougb		name = secretstr;
542170222Sdougb		secretstr = n + 1;
543170222Sdougb		digestbits = parse_hmac(&hmacname, keystr, s - keystr);
544170222Sdougb	} else {
545170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
546170222Sdougb		name = keystr;
547170222Sdougb		n = s;
548170222Sdougb	}
549135446Strhodes
550262706Serwin	isc_buffer_init(&keynamesrc, name, (unsigned int)(n - name));
551262706Serwin	isc_buffer_add(&keynamesrc, (unsigned int)(n - name));
552135446Strhodes
553135446Strhodes	debug("namefromtext");
554224092Sdougb	result = dns_name_fromtext(keyname, &keynamesrc, dns_rootname, 0, NULL);
555135446Strhodes	check_result(result, "dns_name_fromtext");
556135446Strhodes
557135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
558135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
559135446Strhodes	if (secret == NULL)
560135446Strhodes		fatal("out of memory");
561135446Strhodes
562135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
563135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
564135446Strhodes	if (result != ISC_R_SUCCESS) {
565135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
566135446Strhodes			keystr, isc_result_totext(result));
567135446Strhodes		goto failure;
568135446Strhodes	}
569135446Strhodes
570135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
571135446Strhodes
572135446Strhodes	debug("keycreate");
573170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
574218384Sdougb				    ISC_FALSE, NULL, 0, 0, mctx, NULL,
575218384Sdougb				    &tsigkey);
576135446Strhodes	if (result != ISC_R_SUCCESS)
577135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
578135446Strhodes			keystr, dns_result_totext(result));
579170222Sdougb	else
580170222Sdougb		dst_key_setbits(tsigkey->key, digestbits);
581135446Strhodes failure:
582135446Strhodes	if (secret != NULL)
583135446Strhodes		isc_mem_free(mctx, secret);
584135446Strhodes}
585135446Strhodes
586224092Sdougb/*
587224092Sdougb * Get a key from a named.conf format keyfile
588224092Sdougb */
589224092Sdougbstatic isc_result_t
590224092Sdougbread_sessionkey(isc_mem_t *mctx, isc_log_t *lctx) {
591224092Sdougb	cfg_parser_t *pctx = NULL;
592224092Sdougb	cfg_obj_t *sessionkey = NULL;
593224092Sdougb	const cfg_obj_t *key = NULL;
594224092Sdougb	const cfg_obj_t *secretobj = NULL;
595224092Sdougb	const cfg_obj_t *algorithmobj = NULL;
596224092Sdougb	const char *keyname;
597224092Sdougb	const char *secretstr;
598224092Sdougb	const char *algorithm;
599224092Sdougb	isc_result_t result;
600224092Sdougb	int len;
601218384Sdougb
602224092Sdougb	if (! isc_file_exists(keyfile))
603224092Sdougb		return (ISC_R_FILENOTFOUND);
604224092Sdougb
605224092Sdougb	result = cfg_parser_create(mctx, lctx, &pctx);
606224092Sdougb	if (result != ISC_R_SUCCESS)
607224092Sdougb		goto cleanup;
608224092Sdougb
609224092Sdougb	result = cfg_parse_file(pctx, keyfile, &cfg_type_sessionkey,
610224092Sdougb				&sessionkey);
611224092Sdougb	if (result != ISC_R_SUCCESS)
612224092Sdougb		goto cleanup;
613224092Sdougb
614224092Sdougb	result = cfg_map_get(sessionkey, "key", &key);
615224092Sdougb	if (result != ISC_R_SUCCESS)
616224092Sdougb		goto cleanup;
617224092Sdougb
618224092Sdougb	(void) cfg_map_get(key, "secret", &secretobj);
619224092Sdougb	(void) cfg_map_get(key, "algorithm", &algorithmobj);
620224092Sdougb	if (secretobj == NULL || algorithmobj == NULL)
621224092Sdougb		fatal("key must have algorithm and secret");
622224092Sdougb
623224092Sdougb	keyname = cfg_obj_asstring(cfg_map_getname(key));
624224092Sdougb	secretstr = cfg_obj_asstring(secretobj);
625224092Sdougb	algorithm = cfg_obj_asstring(algorithmobj);
626224092Sdougb
627224092Sdougb	len = strlen(algorithm) + strlen(keyname) + strlen(secretstr) + 3;
628224092Sdougb	keystr = isc_mem_allocate(mctx, len);
629224092Sdougb	snprintf(keystr, len, "%s:%s:%s", algorithm, keyname, secretstr);
630224092Sdougb	setup_keystr();
631224092Sdougb
632224092Sdougb cleanup:
633224092Sdougb	if (pctx != NULL) {
634224092Sdougb		if (sessionkey != NULL)
635224092Sdougb			cfg_obj_destroy(pctx, &sessionkey);
636224092Sdougb		cfg_parser_destroy(&pctx);
637224092Sdougb	}
638224092Sdougb
639224092Sdougb	if (keystr != NULL)
640224092Sdougb		isc_mem_free(mctx, keystr);
641224092Sdougb
642224092Sdougb	return (result);
643218384Sdougb}
644218384Sdougb
645135446Strhodesstatic void
646224092Sdougbsetup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) {
647135446Strhodes	dst_key_t *dstkey = NULL;
648135446Strhodes	isc_result_t result;
649170222Sdougb	dns_name_t *hmacname = NULL;
650135446Strhodes
651135446Strhodes	debug("Creating key...");
652135446Strhodes
653218384Sdougb	if (sig0key != NULL)
654218384Sdougb		dst_key_free(&sig0key);
655218384Sdougb
656224092Sdougb	/* Try reading the key from a K* pair */
657224092Sdougb	result = dst_key_fromnamedfile(keyfile, NULL,
658135446Strhodes				       DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx,
659135446Strhodes				       &dstkey);
660224092Sdougb
661224092Sdougb	/* If that didn't work, try reading it as a session.key keyfile */
662135446Strhodes	if (result != ISC_R_SUCCESS) {
663224092Sdougb		result = read_sessionkey(mctx, lctx);
664224092Sdougb		if (result == ISC_R_SUCCESS)
665224092Sdougb			return;
666224092Sdougb	}
667224092Sdougb
668224092Sdougb	if (result != ISC_R_SUCCESS) {
669218384Sdougb		fprintf(stderr, "could not read key from %.*s.{private,key}: "
670218384Sdougb				"%s\n", basenamelen(keyfile), keyfile,
671218384Sdougb				isc_result_totext(result));
672135446Strhodes		return;
673135446Strhodes	}
674224092Sdougb
675170222Sdougb	switch (dst_key_alg(dstkey)) {
676170222Sdougb	case DST_ALG_HMACMD5:
677170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
678170222Sdougb		break;
679170222Sdougb	case DST_ALG_HMACSHA1:
680170222Sdougb		hmacname = DNS_TSIG_HMACSHA1_NAME;
681170222Sdougb		break;
682170222Sdougb	case DST_ALG_HMACSHA224:
683170222Sdougb		hmacname = DNS_TSIG_HMACSHA224_NAME;
684170222Sdougb		break;
685170222Sdougb	case DST_ALG_HMACSHA256:
686170222Sdougb		hmacname = DNS_TSIG_HMACSHA256_NAME;
687170222Sdougb		break;
688170222Sdougb	case DST_ALG_HMACSHA384:
689170222Sdougb		hmacname = DNS_TSIG_HMACSHA384_NAME;
690170222Sdougb		break;
691170222Sdougb	case DST_ALG_HMACSHA512:
692170222Sdougb		hmacname = DNS_TSIG_HMACSHA512_NAME;
693170222Sdougb		break;
694170222Sdougb	}
695170222Sdougb	if (hmacname != NULL) {
696135446Strhodes		result = dns_tsigkey_createfromkey(dst_key_name(dstkey),
697170222Sdougb						   hmacname, dstkey, ISC_FALSE,
698170222Sdougb						   NULL, 0, 0, mctx, NULL,
699170222Sdougb						   &tsigkey);
700218384Sdougb		dst_key_free(&dstkey);
701135446Strhodes		if (result != ISC_R_SUCCESS) {
702135446Strhodes			fprintf(stderr, "could not create key from %s: %s\n",
703135446Strhodes				keyfile, isc_result_totext(result));
704135446Strhodes			return;
705135446Strhodes		}
706222395Sdougb	} else {
707218384Sdougb		dst_key_attach(dstkey, &sig0key);
708222395Sdougb		dst_key_free(&dstkey);
709222395Sdougb	}
710135446Strhodes}
711135446Strhodes
712135446Strhodesstatic void
713135446Strhodesdoshutdown(void) {
714135446Strhodes	isc_task_detach(&global_task);
715135446Strhodes
716135446Strhodes	if (userserver != NULL)
717135446Strhodes		isc_mem_put(mctx, userserver, sizeof(isc_sockaddr_t));
718135446Strhodes
719135446Strhodes	if (localaddr != NULL)
720135446Strhodes		isc_mem_put(mctx, localaddr, sizeof(isc_sockaddr_t));
721135446Strhodes
722135446Strhodes	if (tsigkey != NULL) {
723135446Strhodes		ddebug("Freeing TSIG key");
724135446Strhodes		dns_tsigkey_detach(&tsigkey);
725135446Strhodes	}
726135446Strhodes
727135446Strhodes	if (sig0key != NULL) {
728135446Strhodes		ddebug("Freeing SIG(0) key");
729135446Strhodes		dst_key_free(&sig0key);
730135446Strhodes	}
731135446Strhodes
732135446Strhodes	if (updatemsg != NULL)
733135446Strhodes		dns_message_destroy(&updatemsg);
734135446Strhodes
735135446Strhodes	if (is_dst_up) {
736135446Strhodes		ddebug("Destroy DST lib");
737135446Strhodes		dst_lib_destroy();
738135446Strhodes		is_dst_up = ISC_FALSE;
739135446Strhodes	}
740135446Strhodes
741193149Sdougb	cleanup_entropy(&entropy);
742135446Strhodes
743135446Strhodes	lwres_conf_clear(lwctx);
744135446Strhodes	lwres_context_destroy(&lwctx);
745135446Strhodes
746135446Strhodes	isc_mem_put(mctx, servers, ns_total * sizeof(isc_sockaddr_t));
747135446Strhodes
748135446Strhodes	ddebug("Destroying request manager");
749135446Strhodes	dns_requestmgr_detach(&requestmgr);
750135446Strhodes
751135446Strhodes	ddebug("Freeing the dispatchers");
752135446Strhodes	if (have_ipv4)
753135446Strhodes		dns_dispatch_detach(&dispatchv4);
754135446Strhodes	if (have_ipv6)
755135446Strhodes		dns_dispatch_detach(&dispatchv6);
756135446Strhodes
757135446Strhodes	ddebug("Shutting down dispatch manager");
758135446Strhodes	dns_dispatchmgr_destroy(&dispatchmgr);
759135446Strhodes
760135446Strhodes}
761135446Strhodes
762135446Strhodesstatic void
763135446Strhodesmaybeshutdown(void) {
764135446Strhodes	ddebug("Shutting down request manager");
765135446Strhodes	dns_requestmgr_shutdown(requestmgr);
766135446Strhodes
767135446Strhodes	if (requests != 0)
768135446Strhodes		return;
769135446Strhodes
770135446Strhodes	doshutdown();
771135446Strhodes}
772135446Strhodes
773135446Strhodesstatic void
774135446Strhodesshutdown_program(isc_task_t *task, isc_event_t *event) {
775135446Strhodes	REQUIRE(task == global_task);
776135446Strhodes	UNUSED(task);
777135446Strhodes
778135446Strhodes	ddebug("shutdown_program()");
779135446Strhodes	isc_event_free(&event);
780135446Strhodes
781135446Strhodes	shuttingdown = ISC_TRUE;
782135446Strhodes	maybeshutdown();
783135446Strhodes}
784135446Strhodes
785135446Strhodesstatic void
786135446Strhodessetup_system(void) {
787135446Strhodes	isc_result_t result;
788135446Strhodes	isc_sockaddr_t bind_any, bind_any6;
789135446Strhodes	lwres_result_t lwresult;
790135446Strhodes	unsigned int attrs, attrmask;
791135446Strhodes	int i;
792193149Sdougb	isc_logconfig_t *logconfig = NULL;
793135446Strhodes
794135446Strhodes	ddebug("setup_system()");
795135446Strhodes
796135446Strhodes	dns_result_register();
797135446Strhodes
798135446Strhodes	result = isc_net_probeipv4();
799135446Strhodes	if (result == ISC_R_SUCCESS)
800135446Strhodes		have_ipv4 = ISC_TRUE;
801135446Strhodes
802135446Strhodes	result = isc_net_probeipv6();
803135446Strhodes	if (result == ISC_R_SUCCESS)
804135446Strhodes		have_ipv6 = ISC_TRUE;
805135446Strhodes
806135446Strhodes	if (!have_ipv4 && !have_ipv6)
807135446Strhodes		fatal("could not find either IPv4 or IPv6");
808135446Strhodes
809193149Sdougb	result = isc_log_create(mctx, &lctx, &logconfig);
810193149Sdougb	check_result(result, "isc_log_create");
811135446Strhodes
812193149Sdougb	isc_log_setcontext(lctx);
813193149Sdougb	dns_log_init(lctx);
814193149Sdougb	dns_log_setcontext(lctx);
815193149Sdougb
816193149Sdougb	result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
817193149Sdougb	check_result(result, "isc_log_usechannel");
818193149Sdougb
819193149Sdougb	isc_log_setdebuglevel(lctx, logdebuglevel);
820193149Sdougb
821135446Strhodes	lwresult = lwres_context_create(&lwctx, mctx, mem_alloc, mem_free, 1);
822135446Strhodes	if (lwresult != LWRES_R_SUCCESS)
823135446Strhodes		fatal("lwres_context_create failed");
824135446Strhodes
825135446Strhodes	(void)lwres_conf_parse(lwctx, RESOLV_CONF);
826135446Strhodes	lwconf = lwres_conf_get(lwctx);
827135446Strhodes
828135446Strhodes	ns_total = lwconf->nsnext;
829135446Strhodes	if (ns_total <= 0) {
830135446Strhodes		/* No name servers in resolv.conf; default to loopback. */
831135446Strhodes		struct in_addr localhost;
832135446Strhodes		ns_total = 1;
833135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
834135446Strhodes		if (servers == NULL)
835135446Strhodes			fatal("out of memory");
836135446Strhodes		localhost.s_addr = htonl(INADDR_LOOPBACK);
837224092Sdougb		isc_sockaddr_fromin(&servers[0], &localhost, dnsport);
838135446Strhodes	} else {
839135446Strhodes		servers = isc_mem_get(mctx, ns_total * sizeof(isc_sockaddr_t));
840135446Strhodes		if (servers == NULL)
841135446Strhodes			fatal("out of memory");
842135446Strhodes		for (i = 0; i < ns_total; i++) {
843262706Serwin			if (lwconf->nameservers[i].family == LWRES_ADDRTYPE_V4)
844262706Serwin			{
845135446Strhodes				struct in_addr in4;
846262706Serwin				memmove(&in4,
847262706Serwin					lwconf->nameservers[i].address, 4);
848224092Sdougb				isc_sockaddr_fromin(&servers[i], &in4, dnsport);
849135446Strhodes			} else {
850135446Strhodes				struct in6_addr in6;
851262706Serwin				memmove(&in6,
852262706Serwin					lwconf->nameservers[i].address, 16);
853135446Strhodes				isc_sockaddr_fromin6(&servers[i], &in6,
854224092Sdougb						     dnsport);
855135446Strhodes			}
856135446Strhodes		}
857135446Strhodes	}
858135446Strhodes
859193149Sdougb	setup_entropy(mctx, NULL, &entropy);
860135446Strhodes
861193149Sdougb	result = isc_hash_create(mctx, entropy, DNS_NAME_MAXWIRE);
862135446Strhodes	check_result(result, "isc_hash_create");
863135446Strhodes	isc_hash_init();
864135446Strhodes
865193149Sdougb	result = dns_dispatchmgr_create(mctx, entropy, &dispatchmgr);
866135446Strhodes	check_result(result, "dns_dispatchmgr_create");
867135446Strhodes
868135446Strhodes	result = isc_socketmgr_create(mctx, &socketmgr);
869135446Strhodes	check_result(result, "dns_socketmgr_create");
870135446Strhodes
871135446Strhodes	result = isc_timermgr_create(mctx, &timermgr);
872135446Strhodes	check_result(result, "dns_timermgr_create");
873135446Strhodes
874135446Strhodes	result = isc_taskmgr_create(mctx, 1, 0, &taskmgr);
875135446Strhodes	check_result(result, "isc_taskmgr_create");
876135446Strhodes
877135446Strhodes	result = isc_task_create(taskmgr, 0, &global_task);
878135446Strhodes	check_result(result, "isc_task_create");
879135446Strhodes
880135446Strhodes	result = isc_task_onshutdown(global_task, shutdown_program, NULL);
881135446Strhodes	check_result(result, "isc_task_onshutdown");
882135446Strhodes
883193149Sdougb	result = dst_lib_init(mctx, entropy, 0);
884135446Strhodes	check_result(result, "dst_lib_init");
885135446Strhodes	is_dst_up = ISC_TRUE;
886135446Strhodes
887135446Strhodes	attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
888135446Strhodes	attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
889135446Strhodes
890135446Strhodes	if (have_ipv6) {
891135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
892135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
893135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV6;
894135446Strhodes		isc_sockaddr_any6(&bind_any6);
895135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
896135446Strhodes					     &bind_any6, PACKETSIZE,
897135446Strhodes					     4, 2, 3, 5,
898135446Strhodes					     attrs, attrmask, &dispatchv6);
899135446Strhodes		check_result(result, "dns_dispatch_getudp (v6)");
900135446Strhodes	}
901135446Strhodes
902135446Strhodes	if (have_ipv4) {
903135446Strhodes		attrs = DNS_DISPATCHATTR_UDP;
904135446Strhodes		attrs |= DNS_DISPATCHATTR_MAKEQUERY;
905135446Strhodes		attrs |= DNS_DISPATCHATTR_IPV4;
906135446Strhodes		isc_sockaddr_any(&bind_any);
907135446Strhodes		result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
908135446Strhodes					     &bind_any, PACKETSIZE,
909135446Strhodes					     4, 2, 3, 5,
910135446Strhodes					     attrs, attrmask, &dispatchv4);
911135446Strhodes		check_result(result, "dns_dispatch_getudp (v4)");
912135446Strhodes	}
913135446Strhodes
914135446Strhodes	result = dns_requestmgr_create(mctx, timermgr,
915135446Strhodes				       socketmgr, taskmgr, dispatchmgr,
916135446Strhodes				       dispatchv4, dispatchv6, &requestmgr);
917135446Strhodes	check_result(result, "dns_requestmgr_create");
918135446Strhodes
919135446Strhodes	if (keystr != NULL)
920135446Strhodes		setup_keystr();
921224092Sdougb	else if (local_only) {
922224092Sdougb		result = read_sessionkey(mctx, lctx);
923224092Sdougb		if (result != ISC_R_SUCCESS)
924224092Sdougb			fatal("can't read key from %s: %s\n",
925224092Sdougb			      keyfile, isc_result_totext(result));
926224092Sdougb	} else if (keyfile != NULL)
927224092Sdougb		setup_keyfile(mctx, lctx);
928135446Strhodes}
929135446Strhodes
930135446Strhodesstatic void
931135446Strhodesget_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
932135446Strhodes	int count;
933135446Strhodes	isc_result_t result;
934135446Strhodes
935135446Strhodes	isc_app_block();
936135446Strhodes	result = bind9_getaddresses(host, port, sockaddr, 1, &count);
937135446Strhodes	isc_app_unblock();
938135446Strhodes	if (result != ISC_R_SUCCESS)
939135446Strhodes		fatal("couldn't get address for '%s': %s",
940135446Strhodes		      host, isc_result_totext(result));
941135446Strhodes	INSIST(count == 1);
942135446Strhodes}
943135446Strhodes
944254402Serwin#define PARSE_ARGS_FMT "dDML:y:ghlovk:p:r:R::t:u:"
945193149Sdougb
946135446Strhodesstatic void
947193149Sdougbpre_parse_args(int argc, char **argv) {
948135446Strhodes	int ch;
949193149Sdougb
950193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
951193149Sdougb		switch (ch) {
952193149Sdougb		case 'M': /* was -dm */
953193149Sdougb			debugging = ISC_TRUE;
954193149Sdougb			ddebugging = ISC_TRUE;
955193149Sdougb			memdebugging = ISC_TRUE;
956193149Sdougb			isc_mem_debugging = ISC_MEM_DEBUGTRACE |
957193149Sdougb					    ISC_MEM_DEBUGRECORD;
958193149Sdougb			break;
959193149Sdougb
960193149Sdougb		case '?':
961224092Sdougb		case 'h':
962193149Sdougb			if (isc_commandline_option != '?')
963193149Sdougb				fprintf(stderr, "%s: invalid argument -%c\n",
964193149Sdougb					argv[0], isc_commandline_option);
965224092Sdougb			fprintf(stderr, "usage: nsupdate [-dD] [-L level] [-l]"
966193149Sdougb				"[-g | -o | -y keyname:secret | -k keyfile] "
967193149Sdougb				"[-v] [filename]\n");
968193149Sdougb			exit(1);
969193149Sdougb
970193149Sdougb		default:
971193149Sdougb			break;
972193149Sdougb		}
973193149Sdougb	}
974193149Sdougb	isc_commandline_reset = ISC_TRUE;
975193149Sdougb	isc_commandline_index = 1;
976193149Sdougb}
977193149Sdougb
978193149Sdougbstatic void
979193149Sdougbparse_args(int argc, char **argv, isc_mem_t *mctx, isc_entropy_t **ectx) {
980193149Sdougb	int ch;
981193149Sdougb	isc_uint32_t i;
982135446Strhodes	isc_result_t result;
983135446Strhodes
984135446Strhodes	debug("parse_args");
985193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) {
986135446Strhodes		switch (ch) {
987135446Strhodes		case 'd':
988135446Strhodes			debugging = ISC_TRUE;
989135446Strhodes			break;
990135446Strhodes		case 'D': /* was -dd */
991135446Strhodes			debugging = ISC_TRUE;
992135446Strhodes			ddebugging = ISC_TRUE;
993135446Strhodes			break;
994193149Sdougb		case 'M':
995135446Strhodes			break;
996193149Sdougb		case 'l':
997224092Sdougb			local_only = ISC_TRUE;
998224092Sdougb			break;
999224092Sdougb		case 'L':
1000193149Sdougb			result = isc_parse_uint32(&i, isc_commandline_argument,
1001193149Sdougb						  10);
1002193149Sdougb			if (result != ISC_R_SUCCESS) {
1003193149Sdougb				fprintf(stderr, "bad library debug value "
1004193149Sdougb					"'%s'\n", isc_commandline_argument);
1005193149Sdougb				exit(1);
1006193149Sdougb			}
1007193149Sdougb			logdebuglevel = i;
1008193149Sdougb			break;
1009135446Strhodes		case 'y':
1010135446Strhodes			keystr = isc_commandline_argument;
1011135446Strhodes			break;
1012135446Strhodes		case 'v':
1013135446Strhodes			usevc = ISC_TRUE;
1014135446Strhodes			break;
1015135446Strhodes		case 'k':
1016135446Strhodes			keyfile = isc_commandline_argument;
1017135446Strhodes			break;
1018193149Sdougb		case 'g':
1019193149Sdougb			usegsstsig = ISC_TRUE;
1020193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
1021193149Sdougb			break;
1022193149Sdougb		case 'o':
1023193149Sdougb			usegsstsig = ISC_TRUE;
1024193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
1025193149Sdougb			break;
1026224092Sdougb		case 'p':
1027224092Sdougb			result = isc_parse_uint16(&dnsport,
1028224092Sdougb						  isc_commandline_argument, 10);
1029224092Sdougb			if (result != ISC_R_SUCCESS) {
1030224092Sdougb				fprintf(stderr, "bad port number "
1031224092Sdougb					"'%s'\n", isc_commandline_argument);
1032224092Sdougb				exit(1);
1033224092Sdougb			}
1034224092Sdougb			break;
1035135446Strhodes		case 't':
1036135446Strhodes			result = isc_parse_uint32(&timeout,
1037135446Strhodes						  isc_commandline_argument, 10);
1038135446Strhodes			if (result != ISC_R_SUCCESS) {
1039135446Strhodes				fprintf(stderr, "bad timeout '%s'\n",						isc_commandline_argument);
1040135446Strhodes				exit(1);
1041135446Strhodes			}
1042135446Strhodes			if (timeout == 0)
1043143731Sdougb				timeout = UINT_MAX;
1044135446Strhodes			break;
1045135446Strhodes		case 'u':
1046135446Strhodes			result = isc_parse_uint32(&udp_timeout,
1047135446Strhodes						  isc_commandline_argument, 10);
1048135446Strhodes			if (result != ISC_R_SUCCESS) {
1049135446Strhodes				fprintf(stderr, "bad udp timeout '%s'\n",						isc_commandline_argument);
1050135446Strhodes				exit(1);
1051135446Strhodes			}
1052135446Strhodes			if (udp_timeout == 0)
1053143731Sdougb				udp_timeout = UINT_MAX;
1054135446Strhodes			break;
1055135446Strhodes		case 'r':
1056135446Strhodes			result = isc_parse_uint32(&udp_retries,
1057135446Strhodes						  isc_commandline_argument, 10);
1058135446Strhodes			if (result != ISC_R_SUCCESS) {
1059135446Strhodes				fprintf(stderr, "bad udp retries '%s'\n",						isc_commandline_argument);
1060135446Strhodes				exit(1);
1061135446Strhodes			}
1062135446Strhodes			break;
1063193149Sdougb
1064193149Sdougb		case 'R':
1065193149Sdougb			setup_entropy(mctx, isc_commandline_argument, ectx);
1066193149Sdougb			break;
1067193149Sdougb
1068135446Strhodes		default:
1069193149Sdougb			fprintf(stderr, "%s: unhandled option: %c\n",
1070193149Sdougb				argv[0], isc_commandline_option);
1071135446Strhodes			exit(1);
1072135446Strhodes		}
1073135446Strhodes	}
1074135446Strhodes	if (keyfile != NULL && keystr != NULL) {
1075135446Strhodes		fprintf(stderr, "%s: cannot specify both -k and -y\n",
1076135446Strhodes			argv[0]);
1077135446Strhodes		exit(1);
1078135446Strhodes	}
1079135446Strhodes
1080224092Sdougb	if (local_only) {
1081224092Sdougb		struct in_addr localhost;
1082224092Sdougb
1083224092Sdougb		if (keyfile == NULL)
1084224092Sdougb			keyfile = SESSION_KEYFILE;
1085224092Sdougb
1086224092Sdougb		if (userserver == NULL) {
1087224092Sdougb			userserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1088224092Sdougb			if (userserver == NULL)
1089224092Sdougb				fatal("out of memory");
1090224092Sdougb		}
1091224092Sdougb
1092224092Sdougb		localhost.s_addr = htonl(INADDR_LOOPBACK);
1093224092Sdougb		isc_sockaddr_fromin(userserver, &localhost, dnsport);
1094224092Sdougb	}
1095224092Sdougb
1096193149Sdougb#ifdef GSSAPI
1097193149Sdougb	if (usegsstsig && (keyfile != NULL || keystr != NULL)) {
1098193149Sdougb		fprintf(stderr, "%s: cannot specify -g with -k or -y\n",
1099193149Sdougb			argv[0]);
1100193149Sdougb		exit(1);
1101193149Sdougb	}
1102193149Sdougb#else
1103193149Sdougb	if (usegsstsig) {
1104224092Sdougb		fprintf(stderr, "%s: cannot specify -g	or -o, " \
1105193149Sdougb			"program not linked with GSS API Library\n",
1106193149Sdougb			argv[0]);
1107193149Sdougb		exit(1);
1108193149Sdougb	}
1109193149Sdougb#endif
1110193149Sdougb
1111135446Strhodes	if (argv[isc_commandline_index] != NULL) {
1112135446Strhodes		if (strcmp(argv[isc_commandline_index], "-") == 0) {
1113135446Strhodes			input = stdin;
1114135446Strhodes		} else {
1115135446Strhodes			result = isc_stdio_open(argv[isc_commandline_index],
1116135446Strhodes						"r", &input);
1117135446Strhodes			if (result != ISC_R_SUCCESS) {
1118135446Strhodes				fprintf(stderr, "could not open '%s': %s\n",
1119135446Strhodes					argv[isc_commandline_index],
1120135446Strhodes					isc_result_totext(result));
1121135446Strhodes				exit(1);
1122135446Strhodes			}
1123135446Strhodes		}
1124135446Strhodes		interactive = ISC_FALSE;
1125135446Strhodes	}
1126135446Strhodes}
1127135446Strhodes
1128135446Strhodesstatic isc_uint16_t
1129135446Strhodesparse_name(char **cmdlinep, dns_message_t *msg, dns_name_t **namep) {
1130135446Strhodes	isc_result_t result;
1131135446Strhodes	char *word;
1132135446Strhodes	isc_buffer_t *namebuf = NULL;
1133135446Strhodes	isc_buffer_t source;
1134135446Strhodes
1135135446Strhodes	word = nsu_strsep(cmdlinep, " \t\r\n");
1136245163Serwin	if (word == NULL || *word == 0) {
1137135446Strhodes		fprintf(stderr, "could not read owner name\n");
1138135446Strhodes		return (STATUS_SYNTAX);
1139135446Strhodes	}
1140135446Strhodes
1141135446Strhodes	result = dns_message_gettempname(msg, namep);
1142135446Strhodes	check_result(result, "dns_message_gettempname");
1143135446Strhodes	result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
1144135446Strhodes	check_result(result, "isc_buffer_allocate");
1145135446Strhodes	dns_name_init(*namep, NULL);
1146135446Strhodes	dns_name_setbuffer(*namep, namebuf);
1147135446Strhodes	dns_message_takebuffer(msg, &namebuf);
1148135446Strhodes	isc_buffer_init(&source, word, strlen(word));
1149135446Strhodes	isc_buffer_add(&source, strlen(word));
1150224092Sdougb	result = dns_name_fromtext(*namep, &source, dns_rootname, 0, NULL);
1151135446Strhodes	check_result(result, "dns_name_fromtext");
1152135446Strhodes	isc_buffer_invalidate(&source);
1153135446Strhodes	return (STATUS_MORE);
1154135446Strhodes}
1155135446Strhodes
1156135446Strhodesstatic isc_uint16_t
1157135446Strhodesparse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass,
1158135446Strhodes	    dns_rdatatype_t rdatatype, dns_message_t *msg,
1159135446Strhodes	    dns_rdata_t *rdata)
1160135446Strhodes{
1161135446Strhodes	char *cmdline = *cmdlinep;
1162135446Strhodes	isc_buffer_t source, *buf = NULL, *newbuf = NULL;
1163135446Strhodes	isc_region_t r;
1164135446Strhodes	isc_lex_t *lex = NULL;
1165135446Strhodes	dns_rdatacallbacks_t callbacks;
1166135446Strhodes	isc_result_t result;
1167135446Strhodes
1168245163Serwin	if (cmdline == NULL) {
1169245163Serwin		rdata->flags = DNS_RDATA_UPDATE;
1170245163Serwin		return (STATUS_MORE);
1171245163Serwin	}
1172245163Serwin
1173135446Strhodes	while (*cmdline != 0 && isspace((unsigned char)*cmdline))
1174135446Strhodes		cmdline++;
1175135446Strhodes
1176135446Strhodes	if (*cmdline != 0) {
1177135446Strhodes		dns_rdatacallbacks_init(&callbacks);
1178135446Strhodes		result = isc_lex_create(mctx, strlen(cmdline), &lex);
1179135446Strhodes		check_result(result, "isc_lex_create");
1180135446Strhodes		isc_buffer_init(&source, cmdline, strlen(cmdline));
1181135446Strhodes		isc_buffer_add(&source, strlen(cmdline));
1182135446Strhodes		result = isc_lex_openbuffer(lex, &source);
1183135446Strhodes		check_result(result, "isc_lex_openbuffer");
1184135446Strhodes		result = isc_buffer_allocate(mctx, &buf, MAXWIRE);
1185135446Strhodes		check_result(result, "isc_buffer_allocate");
1186193149Sdougb		result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex,
1187135446Strhodes					    dns_rootname, 0, mctx, buf,
1188135446Strhodes					    &callbacks);
1189135446Strhodes		isc_lex_destroy(&lex);
1190135446Strhodes		if (result == ISC_R_SUCCESS) {
1191135446Strhodes			isc_buffer_usedregion(buf, &r);
1192135446Strhodes			result = isc_buffer_allocate(mctx, &newbuf, r.length);
1193135446Strhodes			check_result(result, "isc_buffer_allocate");
1194135446Strhodes			isc_buffer_putmem(newbuf, r.base, r.length);
1195135446Strhodes			isc_buffer_usedregion(newbuf, &r);
1196135446Strhodes			dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r);
1197135446Strhodes			isc_buffer_free(&buf);
1198135446Strhodes			dns_message_takebuffer(msg, &newbuf);
1199135446Strhodes		} else {
1200135446Strhodes			fprintf(stderr, "invalid rdata format: %s\n",
1201135446Strhodes				isc_result_totext(result));
1202135446Strhodes			isc_buffer_free(&buf);
1203135446Strhodes			return (STATUS_SYNTAX);
1204135446Strhodes		}
1205135446Strhodes	} else {
1206135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1207135446Strhodes	}
1208135446Strhodes	*cmdlinep = cmdline;
1209135446Strhodes	return (STATUS_MORE);
1210135446Strhodes}
1211135446Strhodes
1212135446Strhodesstatic isc_uint16_t
1213135446Strhodesmake_prereq(char *cmdline, isc_boolean_t ispositive, isc_boolean_t isrrset) {
1214135446Strhodes	isc_result_t result;
1215135446Strhodes	char *word;
1216135446Strhodes	dns_name_t *name = NULL;
1217135446Strhodes	isc_textregion_t region;
1218135446Strhodes	dns_rdataset_t *rdataset = NULL;
1219135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1220135446Strhodes	dns_rdataclass_t rdataclass;
1221135446Strhodes	dns_rdatatype_t rdatatype;
1222135446Strhodes	dns_rdata_t *rdata = NULL;
1223135446Strhodes	isc_uint16_t retval;
1224135446Strhodes
1225135446Strhodes	ddebug("make_prereq()");
1226135446Strhodes
1227135446Strhodes	/*
1228135446Strhodes	 * Read the owner name
1229135446Strhodes	 */
1230135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1231135446Strhodes	if (retval != STATUS_MORE)
1232135446Strhodes		return (retval);
1233135446Strhodes
1234135446Strhodes	/*
1235135446Strhodes	 * If this is an rrset prereq, read the class or type.
1236135446Strhodes	 */
1237135446Strhodes	if (isrrset) {
1238135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1239245163Serwin		if (word == NULL || *word == 0) {
1240135446Strhodes			fprintf(stderr, "could not read class or type\n");
1241135446Strhodes			goto failure;
1242135446Strhodes		}
1243135446Strhodes		region.base = word;
1244135446Strhodes		region.length = strlen(word);
1245135446Strhodes		result = dns_rdataclass_fromtext(&rdataclass, &region);
1246135446Strhodes		if (result == ISC_R_SUCCESS) {
1247135446Strhodes			if (!setzoneclass(rdataclass)) {
1248135446Strhodes				fprintf(stderr, "class mismatch: %s\n", word);
1249135446Strhodes				goto failure;
1250135446Strhodes			}
1251135446Strhodes			/*
1252135446Strhodes			 * Now read the type.
1253135446Strhodes			 */
1254135446Strhodes			word = nsu_strsep(&cmdline, " \t\r\n");
1255245163Serwin			if (word == NULL || *word == 0) {
1256135446Strhodes				fprintf(stderr, "could not read type\n");
1257135446Strhodes				goto failure;
1258135446Strhodes			}
1259135446Strhodes			region.base = word;
1260135446Strhodes			region.length = strlen(word);
1261135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1262135446Strhodes			if (result != ISC_R_SUCCESS) {
1263135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1264135446Strhodes				goto failure;
1265135446Strhodes			}
1266135446Strhodes		} else {
1267135446Strhodes			rdataclass = getzoneclass();
1268135446Strhodes			result = dns_rdatatype_fromtext(&rdatatype, &region);
1269135446Strhodes			if (result != ISC_R_SUCCESS) {
1270135446Strhodes				fprintf(stderr, "invalid type: %s\n", word);
1271135446Strhodes				goto failure;
1272135446Strhodes			}
1273135446Strhodes		}
1274135446Strhodes	} else
1275135446Strhodes		rdatatype = dns_rdatatype_any;
1276135446Strhodes
1277135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1278135446Strhodes	check_result(result, "dns_message_gettemprdata");
1279135446Strhodes
1280193149Sdougb	dns_rdata_init(rdata);
1281135446Strhodes
1282135446Strhodes	if (isrrset && ispositive) {
1283135446Strhodes		retval = parse_rdata(&cmdline, rdataclass, rdatatype,
1284135446Strhodes				     updatemsg, rdata);
1285135446Strhodes		if (retval != STATUS_MORE)
1286135446Strhodes			goto failure;
1287135446Strhodes	} else
1288135446Strhodes		rdata->flags = DNS_RDATA_UPDATE;
1289135446Strhodes
1290135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1291135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1292135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1293135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1294135446Strhodes	dns_rdatalist_init(rdatalist);
1295135446Strhodes	rdatalist->type = rdatatype;
1296135446Strhodes	if (ispositive) {
1297135446Strhodes		if (isrrset && rdata->data != NULL)
1298135446Strhodes			rdatalist->rdclass = rdataclass;
1299135446Strhodes		else
1300135446Strhodes			rdatalist->rdclass = dns_rdataclass_any;
1301135446Strhodes	} else
1302135446Strhodes		rdatalist->rdclass = dns_rdataclass_none;
1303135446Strhodes	rdatalist->covers = 0;
1304135446Strhodes	rdatalist->ttl = 0;
1305135446Strhodes	rdata->rdclass = rdatalist->rdclass;
1306135446Strhodes	rdata->type = rdatatype;
1307135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1308135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1309135446Strhodes	dns_rdataset_init(rdataset);
1310135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1311135446Strhodes	ISC_LIST_INIT(name->list);
1312135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1313135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_PREREQUISITE);
1314135446Strhodes	return (STATUS_MORE);
1315135446Strhodes
1316135446Strhodes failure:
1317135446Strhodes	if (name != NULL)
1318135446Strhodes		dns_message_puttempname(updatemsg, &name);
1319135446Strhodes	return (STATUS_SYNTAX);
1320135446Strhodes}
1321135446Strhodes
1322135446Strhodesstatic isc_uint16_t
1323135446Strhodesevaluate_prereq(char *cmdline) {
1324135446Strhodes	char *word;
1325135446Strhodes	isc_boolean_t ispositive, isrrset;
1326135446Strhodes
1327135446Strhodes	ddebug("evaluate_prereq()");
1328135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1329245163Serwin	if (word == NULL || *word == 0) {
1330135446Strhodes		fprintf(stderr, "could not read operation code\n");
1331135446Strhodes		return (STATUS_SYNTAX);
1332135446Strhodes	}
1333135446Strhodes	if (strcasecmp(word, "nxdomain") == 0) {
1334135446Strhodes		ispositive = ISC_FALSE;
1335135446Strhodes		isrrset = ISC_FALSE;
1336135446Strhodes	} else if (strcasecmp(word, "yxdomain") == 0) {
1337135446Strhodes		ispositive = ISC_TRUE;
1338135446Strhodes		isrrset = ISC_FALSE;
1339135446Strhodes	} else if (strcasecmp(word, "nxrrset") == 0) {
1340135446Strhodes		ispositive = ISC_FALSE;
1341135446Strhodes		isrrset = ISC_TRUE;
1342135446Strhodes	} else if (strcasecmp(word, "yxrrset") == 0) {
1343135446Strhodes		ispositive = ISC_TRUE;
1344135446Strhodes		isrrset = ISC_TRUE;
1345135446Strhodes	} else {
1346135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1347135446Strhodes		return (STATUS_SYNTAX);
1348135446Strhodes	}
1349135446Strhodes	return (make_prereq(cmdline, ispositive, isrrset));
1350135446Strhodes}
1351135446Strhodes
1352135446Strhodesstatic isc_uint16_t
1353135446Strhodesevaluate_server(char *cmdline) {
1354135446Strhodes	char *word, *server;
1355135446Strhodes	long port;
1356135446Strhodes
1357224092Sdougb	if (local_only) {
1358224092Sdougb		fprintf(stderr, "cannot reset server in localhost-only mode\n");
1359224092Sdougb		return (STATUS_SYNTAX);
1360224092Sdougb	}
1361224092Sdougb
1362135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1363245163Serwin	if (word == NULL || *word == 0) {
1364135446Strhodes		fprintf(stderr, "could not read server name\n");
1365135446Strhodes		return (STATUS_SYNTAX);
1366135446Strhodes	}
1367135446Strhodes	server = word;
1368135446Strhodes
1369135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1370245163Serwin	if (word == NULL || *word == 0)
1371224092Sdougb		port = dnsport;
1372135446Strhodes	else {
1373135446Strhodes		char *endp;
1374135446Strhodes		port = strtol(word, &endp, 10);
1375135446Strhodes		if (*endp != 0) {
1376135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1377135446Strhodes			return (STATUS_SYNTAX);
1378135446Strhodes		} else if (port < 1 || port > 65535) {
1379135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1380135446Strhodes				"(1 to 65535)\n", word);
1381135446Strhodes			return (STATUS_SYNTAX);
1382135446Strhodes		}
1383135446Strhodes	}
1384135446Strhodes
1385135446Strhodes	if (userserver == NULL) {
1386135446Strhodes		userserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1387135446Strhodes		if (userserver == NULL)
1388135446Strhodes			fatal("out of memory");
1389135446Strhodes	}
1390135446Strhodes
1391135446Strhodes	get_address(server, (in_port_t)port, userserver);
1392135446Strhodes
1393135446Strhodes	return (STATUS_MORE);
1394135446Strhodes}
1395135446Strhodes
1396135446Strhodesstatic isc_uint16_t
1397135446Strhodesevaluate_local(char *cmdline) {
1398135446Strhodes	char *word, *local;
1399135446Strhodes	long port;
1400135446Strhodes	struct in_addr in4;
1401135446Strhodes	struct in6_addr in6;
1402135446Strhodes
1403135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1404245163Serwin	if (word == NULL || *word == 0) {
1405135446Strhodes		fprintf(stderr, "could not read server name\n");
1406135446Strhodes		return (STATUS_SYNTAX);
1407135446Strhodes	}
1408135446Strhodes	local = word;
1409135446Strhodes
1410135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1411245163Serwin	if (word == NULL || *word == 0)
1412135446Strhodes		port = 0;
1413135446Strhodes	else {
1414135446Strhodes		char *endp;
1415135446Strhodes		port = strtol(word, &endp, 10);
1416135446Strhodes		if (*endp != 0) {
1417135446Strhodes			fprintf(stderr, "port '%s' is not numeric\n", word);
1418135446Strhodes			return (STATUS_SYNTAX);
1419135446Strhodes		} else if (port < 1 || port > 65535) {
1420135446Strhodes			fprintf(stderr, "port '%s' is out of range "
1421135446Strhodes				"(1 to 65535)\n", word);
1422135446Strhodes			return (STATUS_SYNTAX);
1423135446Strhodes		}
1424135446Strhodes	}
1425135446Strhodes
1426135446Strhodes	if (localaddr == NULL) {
1427135446Strhodes		localaddr = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
1428135446Strhodes		if (localaddr == NULL)
1429135446Strhodes			fatal("out of memory");
1430135446Strhodes	}
1431135446Strhodes
1432135446Strhodes	if (have_ipv6 && inet_pton(AF_INET6, local, &in6) == 1)
1433135446Strhodes		isc_sockaddr_fromin6(localaddr, &in6, (in_port_t)port);
1434135446Strhodes	else if (have_ipv4 && inet_pton(AF_INET, local, &in4) == 1)
1435135446Strhodes		isc_sockaddr_fromin(localaddr, &in4, (in_port_t)port);
1436135446Strhodes	else {
1437135446Strhodes		fprintf(stderr, "invalid address %s", local);
1438135446Strhodes		return (STATUS_SYNTAX);
1439135446Strhodes	}
1440135446Strhodes
1441135446Strhodes	return (STATUS_MORE);
1442135446Strhodes}
1443135446Strhodes
1444135446Strhodesstatic isc_uint16_t
1445135446Strhodesevaluate_key(char *cmdline) {
1446135446Strhodes	char *namestr;
1447135446Strhodes	char *secretstr;
1448135446Strhodes	isc_buffer_t b;
1449135446Strhodes	isc_result_t result;
1450135446Strhodes	dns_fixedname_t fkeyname;
1451135446Strhodes	dns_name_t *keyname;
1452135446Strhodes	int secretlen;
1453135446Strhodes	unsigned char *secret = NULL;
1454135446Strhodes	isc_buffer_t secretbuf;
1455170222Sdougb	dns_name_t *hmacname = NULL;
1456170222Sdougb	isc_uint16_t digestbits = 0;
1457170222Sdougb	char *n;
1458135446Strhodes
1459135446Strhodes	namestr = nsu_strsep(&cmdline, " \t\r\n");
1460245163Serwin	if (namestr == NULL || *namestr == 0) {
1461135446Strhodes		fprintf(stderr, "could not read key name\n");
1462135446Strhodes		return (STATUS_SYNTAX);
1463135446Strhodes	}
1464135446Strhodes
1465135446Strhodes	dns_fixedname_init(&fkeyname);
1466135446Strhodes	keyname = dns_fixedname_name(&fkeyname);
1467135446Strhodes
1468170222Sdougb	n = strchr(namestr, ':');
1469170222Sdougb	if (n != NULL) {
1470170222Sdougb		digestbits = parse_hmac(&hmacname, namestr, n - namestr);
1471170222Sdougb		namestr = n + 1;
1472170222Sdougb	} else
1473170222Sdougb		hmacname = DNS_TSIG_HMACMD5_NAME;
1474170222Sdougb
1475135446Strhodes	isc_buffer_init(&b, namestr, strlen(namestr));
1476135446Strhodes	isc_buffer_add(&b, strlen(namestr));
1477224092Sdougb	result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL);
1478135446Strhodes	if (result != ISC_R_SUCCESS) {
1479135446Strhodes		fprintf(stderr, "could not parse key name\n");
1480135446Strhodes		return (STATUS_SYNTAX);
1481135446Strhodes	}
1482135446Strhodes
1483135446Strhodes	secretstr = nsu_strsep(&cmdline, "\r\n");
1484245163Serwin	if (secretstr == NULL || *secretstr == 0) {
1485135446Strhodes		fprintf(stderr, "could not read key secret\n");
1486135446Strhodes		return (STATUS_SYNTAX);
1487135446Strhodes	}
1488135446Strhodes	secretlen = strlen(secretstr) * 3 / 4;
1489135446Strhodes	secret = isc_mem_allocate(mctx, secretlen);
1490135446Strhodes	if (secret == NULL)
1491135446Strhodes		fatal("out of memory");
1492186462Sdougb
1493135446Strhodes	isc_buffer_init(&secretbuf, secret, secretlen);
1494135446Strhodes	result = isc_base64_decodestring(secretstr, &secretbuf);
1495135446Strhodes	if (result != ISC_R_SUCCESS) {
1496135446Strhodes		fprintf(stderr, "could not create key from %s: %s\n",
1497135446Strhodes			secretstr, isc_result_totext(result));
1498135446Strhodes		isc_mem_free(mctx, secret);
1499135446Strhodes		return (STATUS_SYNTAX);
1500135446Strhodes	}
1501135446Strhodes	secretlen = isc_buffer_usedlength(&secretbuf);
1502135446Strhodes
1503135446Strhodes	if (tsigkey != NULL)
1504135446Strhodes		dns_tsigkey_detach(&tsigkey);
1505170222Sdougb	result = dns_tsigkey_create(keyname, hmacname, secret, secretlen,
1506218384Sdougb				    ISC_FALSE, NULL, 0, 0, mctx, NULL,
1507170222Sdougb				    &tsigkey);
1508135446Strhodes	isc_mem_free(mctx, secret);
1509135446Strhodes	if (result != ISC_R_SUCCESS) {
1510135446Strhodes		fprintf(stderr, "could not create key from %s %s: %s\n",
1511135446Strhodes			namestr, secretstr, dns_result_totext(result));
1512135446Strhodes		return (STATUS_SYNTAX);
1513135446Strhodes	}
1514170222Sdougb	dst_key_setbits(tsigkey->key, digestbits);
1515135446Strhodes	return (STATUS_MORE);
1516135446Strhodes}
1517135446Strhodes
1518135446Strhodesstatic isc_uint16_t
1519135446Strhodesevaluate_zone(char *cmdline) {
1520135446Strhodes	char *word;
1521135446Strhodes	isc_buffer_t b;
1522135446Strhodes	isc_result_t result;
1523135446Strhodes
1524135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1525245163Serwin	if (word == NULL || *word == 0) {
1526135446Strhodes		fprintf(stderr, "could not read zone name\n");
1527135446Strhodes		return (STATUS_SYNTAX);
1528135446Strhodes	}
1529135446Strhodes
1530135446Strhodes	dns_fixedname_init(&fuserzone);
1531135446Strhodes	userzone = dns_fixedname_name(&fuserzone);
1532135446Strhodes	isc_buffer_init(&b, word, strlen(word));
1533135446Strhodes	isc_buffer_add(&b, strlen(word));
1534224092Sdougb	result = dns_name_fromtext(userzone, &b, dns_rootname, 0, NULL);
1535135446Strhodes	if (result != ISC_R_SUCCESS) {
1536135446Strhodes		userzone = NULL; /* Lest it point to an invalid name */
1537135446Strhodes		fprintf(stderr, "could not parse zone name\n");
1538135446Strhodes		return (STATUS_SYNTAX);
1539135446Strhodes	}
1540135446Strhodes
1541135446Strhodes	return (STATUS_MORE);
1542135446Strhodes}
1543135446Strhodes
1544135446Strhodesstatic isc_uint16_t
1545218384Sdougbevaluate_realm(char *cmdline) {
1546218384Sdougb#ifdef GSSAPI
1547218384Sdougb	char *word;
1548218384Sdougb	char buf[1024];
1549262706Serwin	int n;
1550218384Sdougb
1551262706Serwin	if (realm != NULL) {
1552262706Serwin		isc_mem_free(mctx, realm);
1553218384Sdougb		realm = NULL;
1554218384Sdougb	}
1555218384Sdougb
1556262706Serwin	word = nsu_strsep(&cmdline, " \t\r\n");
1557262706Serwin	if (word == NULL || *word == 0)
1558262706Serwin		return (STATUS_MORE);
1559262706Serwin
1560262706Serwin	n = snprintf(buf, sizeof(buf), "@%s", word);
1561262706Serwin	if (n < 0 || (size_t)n >= sizeof(buf))
1562262706Serwin		fatal("realm is too long");
1563218384Sdougb	realm = isc_mem_strdup(mctx, buf);
1564218384Sdougb	if (realm == NULL)
1565218384Sdougb		fatal("out of memory");
1566218384Sdougb	return (STATUS_MORE);
1567218384Sdougb#else
1568218384Sdougb	UNUSED(cmdline);
1569218384Sdougb	return (STATUS_SYNTAX);
1570218384Sdougb#endif
1571218384Sdougb}
1572218384Sdougb
1573218384Sdougbstatic isc_uint16_t
1574193149Sdougbevaluate_ttl(char *cmdline) {
1575193149Sdougb	char *word;
1576193149Sdougb	isc_result_t result;
1577193149Sdougb	isc_uint32_t ttl;
1578193149Sdougb
1579193149Sdougb	word = nsu_strsep(&cmdline, " \t\r\n");
1580245163Serwin	if (word == NULL || *word == 0) {
1581193149Sdougb		fprintf(stderr, "could not ttl\n");
1582193149Sdougb		return (STATUS_SYNTAX);
1583193149Sdougb	}
1584193149Sdougb
1585193149Sdougb	if (!strcasecmp(word, "none")) {
1586193149Sdougb		default_ttl = 0;
1587193149Sdougb		default_ttl_set = ISC_FALSE;
1588193149Sdougb		return (STATUS_MORE);
1589193149Sdougb	}
1590193149Sdougb
1591193149Sdougb	result = isc_parse_uint32(&ttl, word, 10);
1592193149Sdougb	if (result != ISC_R_SUCCESS)
1593193149Sdougb		return (STATUS_SYNTAX);
1594193149Sdougb
1595193149Sdougb	if (ttl > TTL_MAX) {
1596193149Sdougb		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1597193149Sdougb			word, TTL_MAX);
1598193149Sdougb		return (STATUS_SYNTAX);
1599193149Sdougb	}
1600193149Sdougb	default_ttl = ttl;
1601193149Sdougb	default_ttl_set = ISC_TRUE;
1602193149Sdougb
1603193149Sdougb	return (STATUS_MORE);
1604193149Sdougb}
1605193149Sdougb
1606193149Sdougbstatic isc_uint16_t
1607135446Strhodesevaluate_class(char *cmdline) {
1608135446Strhodes	char *word;
1609135446Strhodes	isc_textregion_t r;
1610135446Strhodes	isc_result_t result;
1611135446Strhodes	dns_rdataclass_t rdclass;
1612135446Strhodes
1613135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1614245163Serwin	if (word == NULL || *word == 0) {
1615135446Strhodes		fprintf(stderr, "could not read class name\n");
1616135446Strhodes		return (STATUS_SYNTAX);
1617135446Strhodes	}
1618135446Strhodes
1619135446Strhodes	r.base = word;
1620186462Sdougb	r.length = strlen(word);
1621186462Sdougb	result = dns_rdataclass_fromtext(&rdclass, &r);
1622135446Strhodes	if (result != ISC_R_SUCCESS) {
1623135446Strhodes		fprintf(stderr, "could not parse class name: %s\n", word);
1624135446Strhodes		return (STATUS_SYNTAX);
1625135446Strhodes	}
1626135446Strhodes	switch (rdclass) {
1627135446Strhodes	case dns_rdataclass_none:
1628135446Strhodes	case dns_rdataclass_any:
1629135446Strhodes	case dns_rdataclass_reserved0:
1630135446Strhodes		fprintf(stderr, "bad default class: %s\n", word);
1631135446Strhodes		return (STATUS_SYNTAX);
1632135446Strhodes	default:
1633135446Strhodes		defaultclass = rdclass;
1634135446Strhodes	}
1635135446Strhodes
1636135446Strhodes	return (STATUS_MORE);
1637135446Strhodes}
1638135446Strhodes
1639135446Strhodesstatic isc_uint16_t
1640135446Strhodesupdate_addordelete(char *cmdline, isc_boolean_t isdelete) {
1641135446Strhodes	isc_result_t result;
1642135446Strhodes	dns_name_t *name = NULL;
1643135446Strhodes	isc_uint32_t ttl;
1644135446Strhodes	char *word;
1645135446Strhodes	dns_rdataclass_t rdataclass;
1646135446Strhodes	dns_rdatatype_t rdatatype;
1647135446Strhodes	dns_rdata_t *rdata = NULL;
1648135446Strhodes	dns_rdatalist_t *rdatalist = NULL;
1649135446Strhodes	dns_rdataset_t *rdataset = NULL;
1650135446Strhodes	isc_textregion_t region;
1651135446Strhodes	isc_uint16_t retval;
1652135446Strhodes
1653135446Strhodes	ddebug("update_addordelete()");
1654135446Strhodes
1655135446Strhodes	/*
1656135446Strhodes	 * Read the owner name.
1657135446Strhodes	 */
1658135446Strhodes	retval = parse_name(&cmdline, updatemsg, &name);
1659135446Strhodes	if (retval != STATUS_MORE)
1660135446Strhodes		return (retval);
1661135446Strhodes
1662135446Strhodes	result = dns_message_gettemprdata(updatemsg, &rdata);
1663135446Strhodes	check_result(result, "dns_message_gettemprdata");
1664135446Strhodes
1665193149Sdougb	dns_rdata_init(rdata);
1666135446Strhodes
1667135446Strhodes	/*
1668135446Strhodes	 * If this is an add, read the TTL and verify that it's in range.
1669135446Strhodes	 * If it's a delete, ignore a TTL if present (for compatibility).
1670135446Strhodes	 */
1671135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1672245163Serwin	if (word == NULL || *word == 0) {
1673135446Strhodes		if (!isdelete) {
1674135446Strhodes			fprintf(stderr, "could not read owner ttl\n");
1675135446Strhodes			goto failure;
1676135446Strhodes		}
1677135446Strhodes		else {
1678135446Strhodes			ttl = 0;
1679135446Strhodes			rdataclass = dns_rdataclass_any;
1680135446Strhodes			rdatatype = dns_rdatatype_any;
1681135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1682135446Strhodes			goto doneparsing;
1683135446Strhodes		}
1684135446Strhodes	}
1685135446Strhodes	result = isc_parse_uint32(&ttl, word, 10);
1686135446Strhodes	if (result != ISC_R_SUCCESS) {
1687135446Strhodes		if (isdelete) {
1688135446Strhodes			ttl = 0;
1689135446Strhodes			goto parseclass;
1690193149Sdougb		} else if (default_ttl_set) {
1691193149Sdougb			ttl = default_ttl;
1692193149Sdougb			goto parseclass;
1693135446Strhodes		} else {
1694135446Strhodes			fprintf(stderr, "ttl '%s': %s\n", word,
1695135446Strhodes				isc_result_totext(result));
1696135446Strhodes			goto failure;
1697135446Strhodes		}
1698135446Strhodes	}
1699135446Strhodes
1700135446Strhodes	if (isdelete)
1701135446Strhodes		ttl = 0;
1702135446Strhodes	else if (ttl > TTL_MAX) {
1703135446Strhodes		fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n",
1704135446Strhodes			word, TTL_MAX);
1705135446Strhodes		goto failure;
1706135446Strhodes	}
1707135446Strhodes
1708135446Strhodes	/*
1709135446Strhodes	 * Read the class or type.
1710135446Strhodes	 */
1711135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1712135446Strhodes parseclass:
1713245163Serwin	if (word == NULL || *word == 0) {
1714135446Strhodes		if (isdelete) {
1715135446Strhodes			rdataclass = dns_rdataclass_any;
1716135446Strhodes			rdatatype = dns_rdatatype_any;
1717135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1718135446Strhodes			goto doneparsing;
1719135446Strhodes		} else {
1720135446Strhodes			fprintf(stderr, "could not read class or type\n");
1721135446Strhodes			goto failure;
1722135446Strhodes		}
1723135446Strhodes	}
1724135446Strhodes	region.base = word;
1725135446Strhodes	region.length = strlen(word);
1726193149Sdougb	rdataclass = dns_rdataclass_any;
1727135446Strhodes	result = dns_rdataclass_fromtext(&rdataclass, &region);
1728193149Sdougb	if (result == ISC_R_SUCCESS && rdataclass != dns_rdataclass_any) {
1729135446Strhodes		if (!setzoneclass(rdataclass)) {
1730135446Strhodes			fprintf(stderr, "class mismatch: %s\n", word);
1731135446Strhodes			goto failure;
1732135446Strhodes		}
1733135446Strhodes		/*
1734135446Strhodes		 * Now read the type.
1735135446Strhodes		 */
1736135446Strhodes		word = nsu_strsep(&cmdline, " \t\r\n");
1737245163Serwin		if (word == NULL || *word == 0) {
1738135446Strhodes			if (isdelete) {
1739135446Strhodes				rdataclass = dns_rdataclass_any;
1740135446Strhodes				rdatatype = dns_rdatatype_any;
1741135446Strhodes				rdata->flags = DNS_RDATA_UPDATE;
1742135446Strhodes				goto doneparsing;
1743135446Strhodes			} else {
1744135446Strhodes				fprintf(stderr, "could not read type\n");
1745135446Strhodes				goto failure;
1746135446Strhodes			}
1747135446Strhodes		}
1748135446Strhodes		region.base = word;
1749135446Strhodes		region.length = strlen(word);
1750135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1751135446Strhodes		if (result != ISC_R_SUCCESS) {
1752135446Strhodes			fprintf(stderr, "'%s' is not a valid type: %s\n",
1753135446Strhodes				word, isc_result_totext(result));
1754135446Strhodes			goto failure;
1755135446Strhodes		}
1756135446Strhodes	} else {
1757135446Strhodes		rdataclass = getzoneclass();
1758135446Strhodes		result = dns_rdatatype_fromtext(&rdatatype, &region);
1759135446Strhodes		if (result != ISC_R_SUCCESS) {
1760135446Strhodes			fprintf(stderr, "'%s' is not a valid class or type: "
1761135446Strhodes				"%s\n", word, isc_result_totext(result));
1762135446Strhodes			goto failure;
1763135446Strhodes		}
1764135446Strhodes	}
1765135446Strhodes
1766135446Strhodes	retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg,
1767135446Strhodes			     rdata);
1768135446Strhodes	if (retval != STATUS_MORE)
1769135446Strhodes		goto failure;
1770135446Strhodes
1771135446Strhodes	if (isdelete) {
1772135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0)
1773135446Strhodes			rdataclass = dns_rdataclass_any;
1774135446Strhodes		else
1775135446Strhodes			rdataclass = dns_rdataclass_none;
1776135446Strhodes	} else {
1777135446Strhodes		if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
1778135446Strhodes			fprintf(stderr, "could not read rdata\n");
1779135446Strhodes			goto failure;
1780135446Strhodes		}
1781135446Strhodes	}
1782135446Strhodes
1783135446Strhodes doneparsing:
1784135446Strhodes
1785135446Strhodes	result = dns_message_gettemprdatalist(updatemsg, &rdatalist);
1786135446Strhodes	check_result(result, "dns_message_gettemprdatalist");
1787135446Strhodes	result = dns_message_gettemprdataset(updatemsg, &rdataset);
1788135446Strhodes	check_result(result, "dns_message_gettemprdataset");
1789135446Strhodes	dns_rdatalist_init(rdatalist);
1790135446Strhodes	rdatalist->type = rdatatype;
1791135446Strhodes	rdatalist->rdclass = rdataclass;
1792135446Strhodes	rdatalist->covers = rdatatype;
1793135446Strhodes	rdatalist->ttl = (dns_ttl_t)ttl;
1794135446Strhodes	ISC_LIST_INIT(rdatalist->rdata);
1795135446Strhodes	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1796135446Strhodes	dns_rdataset_init(rdataset);
1797135446Strhodes	dns_rdatalist_tordataset(rdatalist, rdataset);
1798135446Strhodes	ISC_LIST_INIT(name->list);
1799135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
1800135446Strhodes	dns_message_addname(updatemsg, name, DNS_SECTION_UPDATE);
1801135446Strhodes	return (STATUS_MORE);
1802135446Strhodes
1803135446Strhodes failure:
1804135446Strhodes	if (name != NULL)
1805135446Strhodes		dns_message_puttempname(updatemsg, &name);
1806186462Sdougb	dns_message_puttemprdata(updatemsg, &rdata);
1807135446Strhodes	return (STATUS_SYNTAX);
1808135446Strhodes}
1809135446Strhodes
1810135446Strhodesstatic isc_uint16_t
1811135446Strhodesevaluate_update(char *cmdline) {
1812135446Strhodes	char *word;
1813135446Strhodes	isc_boolean_t isdelete;
1814135446Strhodes
1815135446Strhodes	ddebug("evaluate_update()");
1816135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1817245163Serwin	if (word == NULL || *word == 0) {
1818135446Strhodes		fprintf(stderr, "could not read operation code\n");
1819135446Strhodes		return (STATUS_SYNTAX);
1820135446Strhodes	}
1821135446Strhodes	if (strcasecmp(word, "delete") == 0)
1822135446Strhodes		isdelete = ISC_TRUE;
1823254897Serwin	else if (strcasecmp(word, "del") == 0)
1824254897Serwin		isdelete = ISC_TRUE;
1825135446Strhodes	else if (strcasecmp(word, "add") == 0)
1826135446Strhodes		isdelete = ISC_FALSE;
1827135446Strhodes	else {
1828135446Strhodes		fprintf(stderr, "incorrect operation code: %s\n", word);
1829135446Strhodes		return (STATUS_SYNTAX);
1830135446Strhodes	}
1831135446Strhodes	return (update_addordelete(cmdline, isdelete));
1832135446Strhodes}
1833135446Strhodes
1834135446Strhodesstatic void
1835170222Sdougbsetzone(dns_name_t *zonename) {
1836170222Sdougb	isc_result_t result;
1837170222Sdougb	dns_name_t *name = NULL;
1838170222Sdougb	dns_rdataset_t *rdataset = NULL;
1839170222Sdougb
1840170222Sdougb	result = dns_message_firstname(updatemsg, DNS_SECTION_ZONE);
1841170222Sdougb	if (result == ISC_R_SUCCESS) {
1842170222Sdougb		dns_message_currentname(updatemsg, DNS_SECTION_ZONE, &name);
1843170222Sdougb		dns_message_removename(updatemsg, name, DNS_SECTION_ZONE);
1844170222Sdougb		for (rdataset = ISC_LIST_HEAD(name->list);
1845170222Sdougb		     rdataset != NULL;
1846170222Sdougb		     rdataset = ISC_LIST_HEAD(name->list)) {
1847170222Sdougb			ISC_LIST_UNLINK(name->list, rdataset, link);
1848170222Sdougb			dns_rdataset_disassociate(rdataset);
1849170222Sdougb			dns_message_puttemprdataset(updatemsg, &rdataset);
1850170222Sdougb		}
1851170222Sdougb		dns_message_puttempname(updatemsg, &name);
1852170222Sdougb	}
1853170222Sdougb
1854170222Sdougb	if (zonename != NULL) {
1855170222Sdougb		result = dns_message_gettempname(updatemsg, &name);
1856170222Sdougb		check_result(result, "dns_message_gettempname");
1857170222Sdougb		dns_name_init(name, NULL);
1858170222Sdougb		dns_name_clone(zonename, name);
1859170222Sdougb		result = dns_message_gettemprdataset(updatemsg, &rdataset);
1860170222Sdougb		check_result(result, "dns_message_gettemprdataset");
1861170222Sdougb		dns_rdataset_makequestion(rdataset, getzoneclass(),
1862170222Sdougb					  dns_rdatatype_soa);
1863170222Sdougb		ISC_LIST_INIT(name->list);
1864170222Sdougb		ISC_LIST_APPEND(name->list, rdataset, link);
1865170222Sdougb		dns_message_addname(updatemsg, name, DNS_SECTION_ZONE);
1866170222Sdougb	}
1867170222Sdougb}
1868170222Sdougb
1869170222Sdougbstatic void
1870193149Sdougbshow_message(FILE *stream, dns_message_t *msg, const char *description) {
1871135446Strhodes	isc_result_t result;
1872135446Strhodes	isc_buffer_t *buf = NULL;
1873135446Strhodes	int bufsz;
1874135446Strhodes
1875135446Strhodes	ddebug("show_message()");
1876170222Sdougb
1877170222Sdougb	setzone(userzone);
1878170222Sdougb
1879135446Strhodes	bufsz = INITTEXT;
1880186462Sdougb	do {
1881135446Strhodes		if (bufsz > MAXTEXT) {
1882135446Strhodes			fprintf(stderr, "could not allocate large enough "
1883135446Strhodes				"buffer to display message\n");
1884135446Strhodes			exit(1);
1885135446Strhodes		}
1886135446Strhodes		if (buf != NULL)
1887135446Strhodes			isc_buffer_free(&buf);
1888135446Strhodes		result = isc_buffer_allocate(mctx, &buf, bufsz);
1889135446Strhodes		check_result(result, "isc_buffer_allocate");
1890135446Strhodes		result = dns_message_totext(msg, style, 0, buf);
1891135446Strhodes		bufsz *= 2;
1892135446Strhodes	} while (result == ISC_R_NOSPACE);
1893135446Strhodes	if (result != ISC_R_SUCCESS) {
1894135446Strhodes		fprintf(stderr, "could not convert message to text format.\n");
1895135446Strhodes		isc_buffer_free(&buf);
1896135446Strhodes		return;
1897135446Strhodes	}
1898193149Sdougb	fprintf(stream, "%s\n%.*s", description,
1899193149Sdougb	       (int)isc_buffer_usedlength(buf), (char*)isc_buffer_base(buf));
1900135446Strhodes	isc_buffer_free(&buf);
1901135446Strhodes}
1902135446Strhodes
1903135446Strhodesstatic isc_uint16_t
1904254897Serwindo_next_command(char *cmdline) {
1905135446Strhodes	char *word;
1906135446Strhodes
1907254897Serwin	ddebug("do_next_command()");
1908135446Strhodes	word = nsu_strsep(&cmdline, " \t\r\n");
1909135446Strhodes
1910245163Serwin	if (word == NULL || *word == 0)
1911135446Strhodes		return (STATUS_SEND);
1912135446Strhodes	if (word[0] == ';')
1913135446Strhodes		return (STATUS_MORE);
1914135446Strhodes	if (strcasecmp(word, "quit") == 0)
1915135446Strhodes		return (STATUS_QUIT);
1916135446Strhodes	if (strcasecmp(word, "prereq") == 0)
1917135446Strhodes		return (evaluate_prereq(cmdline));
1918254897Serwin	if (strcasecmp(word, "nxdomain") == 0)
1919254897Serwin		return (make_prereq(cmdline, ISC_FALSE, ISC_FALSE));
1920254897Serwin	if (strcasecmp(word, "yxdomain") == 0)
1921254897Serwin		return (make_prereq(cmdline, ISC_TRUE, ISC_FALSE));
1922254897Serwin	if (strcasecmp(word, "nxrrset") == 0)
1923254897Serwin		return (make_prereq(cmdline, ISC_FALSE, ISC_TRUE));
1924254897Serwin	if (strcasecmp(word, "yxrrset") == 0)
1925254897Serwin		return (make_prereq(cmdline, ISC_TRUE, ISC_TRUE));
1926135446Strhodes	if (strcasecmp(word, "update") == 0)
1927135446Strhodes		return (evaluate_update(cmdline));
1928254897Serwin	if (strcasecmp(word, "delete") == 0)
1929254897Serwin		return (update_addordelete(cmdline, ISC_TRUE));
1930254897Serwin	if (strcasecmp(word, "del") == 0)
1931254897Serwin		return (update_addordelete(cmdline, ISC_TRUE));
1932254897Serwin	if (strcasecmp(word, "add") == 0)
1933254897Serwin		return (update_addordelete(cmdline, ISC_FALSE));
1934135446Strhodes	if (strcasecmp(word, "server") == 0)
1935135446Strhodes		return (evaluate_server(cmdline));
1936135446Strhodes	if (strcasecmp(word, "local") == 0)
1937135446Strhodes		return (evaluate_local(cmdline));
1938135446Strhodes	if (strcasecmp(word, "zone") == 0)
1939135446Strhodes		return (evaluate_zone(cmdline));
1940135446Strhodes	if (strcasecmp(word, "class") == 0)
1941135446Strhodes		return (evaluate_class(cmdline));
1942135446Strhodes	if (strcasecmp(word, "send") == 0)
1943135446Strhodes		return (STATUS_SEND);
1944193149Sdougb	if (strcasecmp(word, "debug") == 0) {
1945193149Sdougb		if (debugging)
1946193149Sdougb			ddebugging = ISC_TRUE;
1947193149Sdougb		else
1948193149Sdougb			debugging = ISC_TRUE;
1949193149Sdougb		return (STATUS_MORE);
1950193149Sdougb	}
1951193149Sdougb	if (strcasecmp(word, "ttl") == 0)
1952193149Sdougb		return (evaluate_ttl(cmdline));
1953135446Strhodes	if (strcasecmp(word, "show") == 0) {
1954193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
1955135446Strhodes		return (STATUS_MORE);
1956135446Strhodes	}
1957135446Strhodes	if (strcasecmp(word, "answer") == 0) {
1958135446Strhodes		if (answer != NULL)
1959193149Sdougb			show_message(stdout, answer, "Answer:");
1960135446Strhodes		return (STATUS_MORE);
1961135446Strhodes	}
1962193149Sdougb	if (strcasecmp(word, "key") == 0) {
1963193149Sdougb		usegsstsig = ISC_FALSE;
1964135446Strhodes		return (evaluate_key(cmdline));
1965193149Sdougb	}
1966218384Sdougb	if (strcasecmp(word, "realm") == 0)
1967218384Sdougb		return (evaluate_realm(cmdline));
1968193149Sdougb	if (strcasecmp(word, "gsstsig") == 0) {
1969193149Sdougb#ifdef GSSAPI
1970193149Sdougb		usegsstsig = ISC_TRUE;
1971193149Sdougb		use_win2k_gsstsig = ISC_FALSE;
1972193149Sdougb#else
1973193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1974193149Sdougb#endif
1975193149Sdougb		return (STATUS_MORE);
1976193149Sdougb	}
1977193149Sdougb	if (strcasecmp(word, "oldgsstsig") == 0) {
1978193149Sdougb#ifdef GSSAPI
1979193149Sdougb		usegsstsig = ISC_TRUE;
1980193149Sdougb		use_win2k_gsstsig = ISC_TRUE;
1981193149Sdougb#else
1982193149Sdougb		fprintf(stderr, "gsstsig not supported\n");
1983193149Sdougb#endif
1984193149Sdougb		return (STATUS_MORE);
1985193149Sdougb	}
1986193149Sdougb	if (strcasecmp(word, "help") == 0) {
1987193149Sdougb		fprintf(stdout,
1988193149Sdougb"local address [port]      (set local resolver)\n"
1989193149Sdougb"server address [port]     (set master server for zone)\n"
1990193149Sdougb"send                      (send the update request)\n"
1991193149Sdougb"show                      (show the update request)\n"
1992224092Sdougb"answer                    (show the answer to the last request)\n"
1993193149Sdougb"quit                      (quit, any pending update is not sent\n"
1994224092Sdougb"help                      (display this message_\n"
1995193149Sdougb"key [hmac:]keyname secret (use TSIG to sign the request)\n"
1996193149Sdougb"gsstsig                   (use GSS_TSIG to sign the request)\n"
1997193149Sdougb"oldgsstsig                (use Microsoft's GSS_TSIG to sign the request)\n"
1998193149Sdougb"zone name                 (set the zone to be updated)\n"
1999193149Sdougb"class CLASS               (set the zone's DNS class, e.g. IN (default), CH)\n"
2000254897Serwin"[prereq] nxdomain name    (does this name not exist)\n"
2001254897Serwin"[prereq] yxdomain name    (does this name exist)\n"
2002254897Serwin"[prereq] nxrrset ....     (does this RRset exist)\n"
2003254897Serwin"[prereq] yxrrset ....     (does this RRset not exist)\n"
2004254897Serwin"[update] add ....         (add the given record to the zone)\n"
2005254897Serwin"[update] del[ete] ....    (remove the given record(s) from the zone)\n");
2006193149Sdougb		return (STATUS_MORE);
2007193149Sdougb	}
2008135446Strhodes	fprintf(stderr, "incorrect section name: %s\n", word);
2009135446Strhodes	return (STATUS_SYNTAX);
2010135446Strhodes}
2011135446Strhodes
2012254897Serwinstatic isc_uint16_t
2013254897Serwinget_next_command(void) {
2014254897Serwin	isc_uint16_t result = STATUS_QUIT;
2015254897Serwin	char cmdlinebuf[MAXCMD];
2016254897Serwin	char *cmdline;
2017254897Serwin
2018254897Serwin	isc_app_block();
2019254897Serwin	if (interactive) {
2020254897Serwin#ifdef HAVE_READLINE
2021254897Serwin		cmdline = readline("> ");
2022262706Serwin		if (cmdline != NULL)
2023262706Serwin			add_history(cmdline);
2024254897Serwin#else
2025254897Serwin		fprintf(stdout, "> ");
2026254897Serwin		fflush(stdout);
2027254897Serwin		cmdline = fgets(cmdlinebuf, MAXCMD, input);
2028254897Serwin#endif
2029254897Serwin	} else
2030254897Serwin		cmdline = fgets(cmdlinebuf, MAXCMD, input);
2031254897Serwin	isc_app_unblock();
2032254897Serwin
2033254897Serwin	if (cmdline != NULL) {
2034254897Serwin		char *tmp = cmdline;
2035254897Serwin
2036254897Serwin		/*
2037254897Serwin		 * Normalize input by removing any eol as readline()
2038254897Serwin		 * removes eol but fgets doesn't.
2039254897Serwin		 */
2040254897Serwin		(void)nsu_strsep(&tmp, "\r\n");
2041254897Serwin		result = do_next_command(cmdline);
2042254897Serwin	}
2043254897Serwin#ifdef HAVE_READLINE
2044254897Serwin	if (interactive)
2045254897Serwin		free(cmdline);
2046254897Serwin#endif
2047254897Serwin	return (result);
2048254897Serwin}
2049254897Serwin
2050135446Strhodesstatic isc_boolean_t
2051135446Strhodesuser_interaction(void) {
2052135446Strhodes	isc_uint16_t result = STATUS_MORE;
2053135446Strhodes
2054135446Strhodes	ddebug("user_interaction()");
2055174187Sdougb	while ((result == STATUS_MORE) || (result == STATUS_SYNTAX)) {
2056135446Strhodes		result = get_next_command();
2057174187Sdougb		if (!interactive && result == STATUS_SYNTAX)
2058174187Sdougb			fatal("syntax error");
2059174187Sdougb	}
2060135446Strhodes	if (result == STATUS_SEND)
2061135446Strhodes		return (ISC_TRUE);
2062135446Strhodes	return (ISC_FALSE);
2063135446Strhodes
2064135446Strhodes}
2065135446Strhodes
2066135446Strhodesstatic void
2067135446Strhodesdone_update(void) {
2068135446Strhodes	isc_event_t *event = global_event;
2069135446Strhodes	ddebug("done_update()");
2070135446Strhodes	isc_task_send(global_task, &event);
2071135446Strhodes}
2072135446Strhodes
2073135446Strhodesstatic void
2074135446Strhodescheck_tsig_error(dns_rdataset_t *rdataset, isc_buffer_t *b) {
2075135446Strhodes	isc_result_t result;
2076135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2077135446Strhodes	dns_rdata_any_tsig_t tsig;
2078135446Strhodes
2079135446Strhodes	result = dns_rdataset_first(rdataset);
2080135446Strhodes	check_result(result, "dns_rdataset_first");
2081135446Strhodes	dns_rdataset_current(rdataset, &rdata);
2082135446Strhodes	result = dns_rdata_tostruct(&rdata, &tsig, NULL);
2083135446Strhodes	check_result(result, "dns_rdata_tostruct");
2084135446Strhodes	if (tsig.error != 0) {
2085135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
2086135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
2087135446Strhodes		isc__buffer_putstr(b, "(" /*)*/);
2088135446Strhodes		result = dns_tsigrcode_totext(tsig.error, b);
2089135446Strhodes		check_result(result, "dns_tsigrcode_totext");
2090135446Strhodes		if (isc_buffer_remaininglength(b) < 1)
2091135446Strhodes		      check_result(ISC_R_NOSPACE, "isc_buffer_remaininglength");
2092135446Strhodes		isc__buffer_putstr(b,  /*(*/ ")");
2093135446Strhodes	}
2094135446Strhodes}
2095135446Strhodes
2096135446Strhodesstatic void
2097135446Strhodesupdate_completed(isc_task_t *task, isc_event_t *event) {
2098135446Strhodes	dns_requestevent_t *reqev = NULL;
2099135446Strhodes	isc_result_t result;
2100135446Strhodes	dns_request_t *request;
2101135446Strhodes
2102135446Strhodes	UNUSED(task);
2103135446Strhodes
2104135446Strhodes	ddebug("update_completed()");
2105135446Strhodes
2106135446Strhodes	requests--;
2107135446Strhodes
2108135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2109135446Strhodes	reqev = (dns_requestevent_t *)event;
2110135446Strhodes	request = reqev->request;
2111135446Strhodes
2112135446Strhodes	if (shuttingdown) {
2113135446Strhodes		dns_request_destroy(&request);
2114135446Strhodes		isc_event_free(&event);
2115135446Strhodes		maybeshutdown();
2116135446Strhodes		return;
2117135446Strhodes	}
2118135446Strhodes
2119135446Strhodes	if (reqev->result != ISC_R_SUCCESS) {
2120135446Strhodes		fprintf(stderr, "; Communication with server failed: %s\n",
2121135446Strhodes			isc_result_totext(reqev->result));
2122135446Strhodes		seenerror = ISC_TRUE;
2123135446Strhodes		goto done;
2124135446Strhodes	}
2125135446Strhodes
2126135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &answer);
2127135446Strhodes	check_result(result, "dns_message_create");
2128135446Strhodes	result = dns_request_getresponse(request, answer,
2129135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
2130135446Strhodes	switch (result) {
2131135446Strhodes	case ISC_R_SUCCESS:
2132193149Sdougb		if (answer->verify_attempted)
2133193149Sdougb			ddebug("tsig verification successful");
2134135446Strhodes		break;
2135135446Strhodes	case DNS_R_CLOCKSKEW:
2136135446Strhodes	case DNS_R_EXPECTEDTSIG:
2137135446Strhodes	case DNS_R_TSIGERRORSET:
2138135446Strhodes	case DNS_R_TSIGVERIFYFAILURE:
2139135446Strhodes	case DNS_R_UNEXPECTEDTSIG:
2140193149Sdougb	case ISC_R_FAILURE:
2141193149Sdougb#if 0
2142193149Sdougb		if (usegsstsig && answer->rcode == dns_rcode_noerror) {
2143193149Sdougb			/*
2144193149Sdougb			 * For MS DNS that violates RFC 2845, section 4.2
2145193149Sdougb			 */
2146193149Sdougb			break;
2147193149Sdougb		}
2148193149Sdougb#endif
2149135446Strhodes		fprintf(stderr, "; TSIG error with server: %s\n",
2150135446Strhodes			isc_result_totext(result));
2151135446Strhodes		seenerror = ISC_TRUE;
2152135446Strhodes		break;
2153135446Strhodes	default:
2154135446Strhodes		check_result(result, "dns_request_getresponse");
2155135446Strhodes	}
2156135446Strhodes
2157135446Strhodes	if (answer->rcode != dns_rcode_noerror) {
2158135446Strhodes		seenerror = ISC_TRUE;
2159135446Strhodes		if (!debugging) {
2160135446Strhodes			char buf[64];
2161135446Strhodes			isc_buffer_t b;
2162135446Strhodes			dns_rdataset_t *rds;
2163186462Sdougb
2164135446Strhodes			isc_buffer_init(&b, buf, sizeof(buf) - 1);
2165135446Strhodes			result = dns_rcode_totext(answer->rcode, &b);
2166135446Strhodes			check_result(result, "dns_rcode_totext");
2167135446Strhodes			rds = dns_message_gettsig(answer, NULL);
2168135446Strhodes			if (rds != NULL)
2169135446Strhodes				check_tsig_error(rds, &b);
2170135446Strhodes			fprintf(stderr, "update failed: %.*s\n",
2171135446Strhodes				(int)isc_buffer_usedlength(&b), buf);
2172135446Strhodes		}
2173135446Strhodes	}
2174193149Sdougb	if (debugging)
2175193149Sdougb		show_message(stderr, answer, "\nReply from update query:");
2176135446Strhodes
2177135446Strhodes done:
2178135446Strhodes	dns_request_destroy(&request);
2179193149Sdougb	if (usegsstsig) {
2180193149Sdougb		dns_name_free(&tmpzonename, mctx);
2181193149Sdougb		dns_name_free(&restart_master, mctx);
2182193149Sdougb	}
2183135446Strhodes	isc_event_free(&event);
2184135446Strhodes	done_update();
2185135446Strhodes}
2186135446Strhodes
2187135446Strhodesstatic void
2188135446Strhodessend_update(dns_name_t *zonename, isc_sockaddr_t *master,
2189135446Strhodes	    isc_sockaddr_t *srcaddr)
2190135446Strhodes{
2191135446Strhodes	isc_result_t result;
2192135446Strhodes	dns_request_t *request = NULL;
2193224092Sdougb	unsigned int options = DNS_REQUESTOPT_CASE;
2194135446Strhodes
2195135446Strhodes	ddebug("send_update()");
2196135446Strhodes
2197170222Sdougb	setzone(zonename);
2198135446Strhodes
2199135446Strhodes	if (usevc)
2200135446Strhodes		options |= DNS_REQUESTOPT_TCP;
2201135446Strhodes	if (tsigkey == NULL && sig0key != NULL) {
2202135446Strhodes		result = dns_message_setsig0key(updatemsg, sig0key);
2203135446Strhodes		check_result(result, "dns_message_setsig0key");
2204135446Strhodes	}
2205135446Strhodes	if (debugging) {
2206135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2207135446Strhodes
2208135446Strhodes		isc_sockaddr_format(master, addrbuf, sizeof(addrbuf));
2209135446Strhodes		fprintf(stderr, "Sending update to %s\n", addrbuf);
2210135446Strhodes	}
2211193149Sdougb
2212218384Sdougb	/* Windows doesn't like the tsig name to be compressed. */
2213218384Sdougb	if (updatemsg->tsigname)
2214218384Sdougb		updatemsg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2215218384Sdougb
2216135446Strhodes	result = dns_request_createvia3(requestmgr, updatemsg, srcaddr,
2217135446Strhodes					master, options, tsigkey, timeout,
2218135446Strhodes					udp_timeout, udp_retries, global_task,
2219135446Strhodes					update_completed, NULL, &request);
2220135446Strhodes	check_result(result, "dns_request_createvia3");
2221135446Strhodes
2222135446Strhodes	if (debugging)
2223193149Sdougb		show_message(stdout, updatemsg, "Outgoing update query:");
2224135446Strhodes
2225135446Strhodes	requests++;
2226135446Strhodes}
2227135446Strhodes
2228135446Strhodesstatic void
2229135446Strhodesrecvsoa(isc_task_t *task, isc_event_t *event) {
2230135446Strhodes	dns_requestevent_t *reqev = NULL;
2231135446Strhodes	dns_request_t *request = NULL;
2232135446Strhodes	isc_result_t result, eresult;
2233135446Strhodes	dns_message_t *rcvmsg = NULL;
2234135446Strhodes	dns_section_t section;
2235135446Strhodes	dns_name_t *name = NULL;
2236135446Strhodes	dns_rdataset_t *soaset = NULL;
2237135446Strhodes	dns_rdata_soa_t soa;
2238135446Strhodes	dns_rdata_t soarr = DNS_RDATA_INIT;
2239135446Strhodes	int pass = 0;
2240135446Strhodes	dns_name_t master;
2241135446Strhodes	nsu_requestinfo_t *reqinfo;
2242135446Strhodes	dns_message_t *soaquery = NULL;
2243135446Strhodes	isc_sockaddr_t *addr;
2244135446Strhodes	isc_boolean_t seencname = ISC_FALSE;
2245143731Sdougb	dns_name_t tname;
2246143731Sdougb	unsigned int nlabels;
2247135446Strhodes
2248135446Strhodes	UNUSED(task);
2249135446Strhodes
2250135446Strhodes	ddebug("recvsoa()");
2251135446Strhodes
2252135446Strhodes	requests--;
2253186462Sdougb
2254135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2255135446Strhodes	reqev = (dns_requestevent_t *)event;
2256135446Strhodes	request = reqev->request;
2257135446Strhodes	eresult = reqev->result;
2258135446Strhodes	reqinfo = reqev->ev_arg;
2259135446Strhodes	soaquery = reqinfo->msg;
2260135446Strhodes	addr = reqinfo->addr;
2261135446Strhodes
2262135446Strhodes	if (shuttingdown) {
2263135446Strhodes		dns_request_destroy(&request);
2264135446Strhodes		dns_message_destroy(&soaquery);
2265135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2266135446Strhodes		isc_event_free(&event);
2267135446Strhodes		maybeshutdown();
2268135446Strhodes		return;
2269135446Strhodes	}
2270135446Strhodes
2271135446Strhodes	if (eresult != ISC_R_SUCCESS) {
2272135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2273135446Strhodes
2274135446Strhodes		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2275135446Strhodes		fprintf(stderr, "; Communication with %s failed: %s\n",
2276193149Sdougb			addrbuf, isc_result_totext(eresult));
2277135446Strhodes		if (userserver != NULL)
2278135446Strhodes			fatal("could not talk to specified name server");
2279135446Strhodes		else if (++ns_inuse >= lwconf->nsnext)
2280135446Strhodes			fatal("could not talk to any default name server");
2281135446Strhodes		ddebug("Destroying request [%p]", request);
2282135446Strhodes		dns_request_destroy(&request);
2283135446Strhodes		dns_message_renderreset(soaquery);
2284153816Sdougb		dns_message_settsigkey(soaquery, NULL);
2285135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2286135446Strhodes		isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2287135446Strhodes		isc_event_free(&event);
2288135446Strhodes		setzoneclass(dns_rdataclass_none);
2289135446Strhodes		return;
2290135446Strhodes	}
2291170222Sdougb
2292135446Strhodes	isc_mem_put(mctx, reqinfo, sizeof(nsu_requestinfo_t));
2293170222Sdougb	reqinfo = NULL;
2294135446Strhodes	isc_event_free(&event);
2295135446Strhodes	reqev = NULL;
2296135446Strhodes
2297135446Strhodes	ddebug("About to create rcvmsg");
2298135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2299135446Strhodes	check_result(result, "dns_message_create");
2300135446Strhodes	result = dns_request_getresponse(request, rcvmsg,
2301135446Strhodes					 DNS_MESSAGEPARSE_PRESERVEORDER);
2302135446Strhodes	if (result == DNS_R_TSIGERRORSET && userserver != NULL) {
2303135446Strhodes		dns_message_destroy(&rcvmsg);
2304135446Strhodes		ddebug("Destroying request [%p]", request);
2305135446Strhodes		dns_request_destroy(&request);
2306135446Strhodes		reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2307135446Strhodes		if (reqinfo == NULL)
2308135446Strhodes			fatal("out of memory");
2309135446Strhodes		reqinfo->msg = soaquery;
2310135446Strhodes		reqinfo->addr = addr;
2311135446Strhodes		dns_message_renderreset(soaquery);
2312135446Strhodes		ddebug("retrying soa request without TSIG");
2313135446Strhodes		result = dns_request_createvia3(requestmgr, soaquery,
2314135446Strhodes						localaddr, addr, 0, NULL,
2315135446Strhodes						FIND_TIMEOUT * 20,
2316165071Sdougb						FIND_TIMEOUT, 3,
2317135446Strhodes						global_task, recvsoa, reqinfo,
2318135446Strhodes						&request);
2319135446Strhodes		check_result(result, "dns_request_createvia");
2320135446Strhodes		requests++;
2321135446Strhodes		return;
2322135446Strhodes	}
2323135446Strhodes	check_result(result, "dns_request_getresponse");
2324135446Strhodes	section = DNS_SECTION_ANSWER;
2325225361Sdougb	POST(section);
2326193149Sdougb	if (debugging)
2327193149Sdougb		show_message(stderr, rcvmsg, "Reply from SOA query:");
2328135446Strhodes
2329135446Strhodes	if (rcvmsg->rcode != dns_rcode_noerror &&
2330135446Strhodes	    rcvmsg->rcode != dns_rcode_nxdomain)
2331135446Strhodes		fatal("response to SOA query was unsuccessful");
2332135446Strhodes
2333170222Sdougb	if (userzone != NULL && rcvmsg->rcode == dns_rcode_nxdomain) {
2334170222Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
2335170222Sdougb		dns_name_format(userzone, namebuf, sizeof(namebuf));
2336170222Sdougb		error("specified zone '%s' does not exist (NXDOMAIN)",
2337170222Sdougb		      namebuf);
2338170222Sdougb		dns_message_destroy(&rcvmsg);
2339170222Sdougb		dns_request_destroy(&request);
2340170222Sdougb		dns_message_destroy(&soaquery);
2341170222Sdougb		ddebug("Out of recvsoa");
2342170222Sdougb		done_update();
2343234010Sdougb		seenerror = ISC_TRUE;
2344170222Sdougb		return;
2345170222Sdougb	}
2346170222Sdougb
2347135446Strhodes lookforsoa:
2348135446Strhodes	if (pass == 0)
2349135446Strhodes		section = DNS_SECTION_ANSWER;
2350135446Strhodes	else if (pass == 1)
2351135446Strhodes		section = DNS_SECTION_AUTHORITY;
2352186462Sdougb	else
2353143731Sdougb		goto droplabel;
2354135446Strhodes
2355135446Strhodes	result = dns_message_firstname(rcvmsg, section);
2356135446Strhodes	if (result != ISC_R_SUCCESS) {
2357135446Strhodes		pass++;
2358135446Strhodes		goto lookforsoa;
2359135446Strhodes	}
2360135446Strhodes	while (result == ISC_R_SUCCESS) {
2361135446Strhodes		name = NULL;
2362135446Strhodes		dns_message_currentname(rcvmsg, section, &name);
2363135446Strhodes		soaset = NULL;
2364135446Strhodes		result = dns_message_findtype(name, dns_rdatatype_soa, 0,
2365135446Strhodes					      &soaset);
2366135446Strhodes		if (result == ISC_R_SUCCESS)
2367135446Strhodes			break;
2368135446Strhodes		if (section == DNS_SECTION_ANSWER) {
2369135446Strhodes			dns_rdataset_t *tset = NULL;
2370135446Strhodes			if (dns_message_findtype(name, dns_rdatatype_cname, 0,
2371193149Sdougb						 &tset) == ISC_R_SUCCESS ||
2372135446Strhodes			    dns_message_findtype(name, dns_rdatatype_dname, 0,
2373193149Sdougb						 &tset) == ISC_R_SUCCESS ) {
2374135446Strhodes				seencname = ISC_TRUE;
2375135446Strhodes				break;
2376135446Strhodes			}
2377135446Strhodes		}
2378186462Sdougb
2379135446Strhodes		result = dns_message_nextname(rcvmsg, section);
2380135446Strhodes	}
2381135446Strhodes
2382135446Strhodes	if (soaset == NULL && !seencname) {
2383135446Strhodes		pass++;
2384135446Strhodes		goto lookforsoa;
2385135446Strhodes	}
2386135446Strhodes
2387143731Sdougb	if (seencname)
2388143731Sdougb		goto droplabel;
2389135446Strhodes
2390135446Strhodes	if (debugging) {
2391135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2392135446Strhodes		dns_name_format(name, namestr, sizeof(namestr));
2393135446Strhodes		fprintf(stderr, "Found zone name: %s\n", namestr);
2394135446Strhodes	}
2395135446Strhodes
2396135446Strhodes	result = dns_rdataset_first(soaset);
2397135446Strhodes	check_result(result, "dns_rdataset_first");
2398135446Strhodes
2399135446Strhodes	dns_rdata_init(&soarr);
2400135446Strhodes	dns_rdataset_current(soaset, &soarr);
2401135446Strhodes	result = dns_rdata_tostruct(&soarr, &soa, NULL);
2402135446Strhodes	check_result(result, "dns_rdata_tostruct");
2403135446Strhodes
2404135446Strhodes	dns_name_init(&master, NULL);
2405135446Strhodes	dns_name_clone(&soa.origin, &master);
2406135446Strhodes
2407135446Strhodes	if (userzone != NULL)
2408135446Strhodes		zonename = userzone;
2409135446Strhodes	else
2410135446Strhodes		zonename = name;
2411135446Strhodes
2412135446Strhodes	if (debugging) {
2413135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
2414135446Strhodes		dns_name_format(&master, namestr, sizeof(namestr));
2415135446Strhodes		fprintf(stderr, "The master is: %s\n", namestr);
2416135446Strhodes	}
2417135446Strhodes
2418135446Strhodes	if (userserver != NULL)
2419135446Strhodes		serveraddr = userserver;
2420135446Strhodes	else {
2421135446Strhodes		char serverstr[DNS_NAME_MAXTEXT+1];
2422135446Strhodes		isc_buffer_t buf;
2423135446Strhodes
2424135446Strhodes		isc_buffer_init(&buf, serverstr, sizeof(serverstr));
2425135446Strhodes		result = dns_name_totext(&master, ISC_TRUE, &buf);
2426135446Strhodes		check_result(result, "dns_name_totext");
2427135446Strhodes		serverstr[isc_buffer_usedlength(&buf)] = 0;
2428224092Sdougb		get_address(serverstr, dnsport, &tempaddr);
2429135446Strhodes		serveraddr = &tempaddr;
2430135446Strhodes	}
2431143731Sdougb	dns_rdata_freestruct(&soa);
2432135446Strhodes
2433193149Sdougb#ifdef GSSAPI
2434193149Sdougb	if (usegsstsig) {
2435193149Sdougb		dns_name_init(&tmpzonename, NULL);
2436193149Sdougb		dns_name_dup(zonename, mctx, &tmpzonename);
2437193149Sdougb		dns_name_init(&restart_master, NULL);
2438193149Sdougb		dns_name_dup(&master, mctx, &restart_master);
2439193149Sdougb		start_gssrequest(&master);
2440193149Sdougb	} else {
2441193149Sdougb		send_update(zonename, serveraddr, localaddr);
2442193149Sdougb		setzoneclass(dns_rdataclass_none);
2443193149Sdougb	}
2444193149Sdougb#else
2445135446Strhodes	send_update(zonename, serveraddr, localaddr);
2446143731Sdougb	setzoneclass(dns_rdataclass_none);
2447193149Sdougb#endif
2448135446Strhodes
2449135446Strhodes	dns_message_destroy(&soaquery);
2450135446Strhodes	dns_request_destroy(&request);
2451135446Strhodes
2452135446Strhodes out:
2453135446Strhodes	dns_message_destroy(&rcvmsg);
2454135446Strhodes	ddebug("Out of recvsoa");
2455143731Sdougb	return;
2456186462Sdougb
2457143731Sdougb droplabel:
2458143731Sdougb	result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
2459143731Sdougb	INSIST(result == ISC_R_SUCCESS);
2460143731Sdougb	name = NULL;
2461143731Sdougb	dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
2462143731Sdougb	nlabels = dns_name_countlabels(name);
2463143731Sdougb	if (nlabels == 1)
2464143731Sdougb		fatal("could not find enclosing zone");
2465143731Sdougb	dns_name_init(&tname, NULL);
2466143731Sdougb	dns_name_getlabelsequence(name, 1, nlabels - 1, &tname);
2467143731Sdougb	dns_name_clone(&tname, name);
2468143731Sdougb	dns_request_destroy(&request);
2469143731Sdougb	dns_message_renderreset(soaquery);
2470153816Sdougb	dns_message_settsigkey(soaquery, NULL);
2471143731Sdougb	if (userserver != NULL)
2472143731Sdougb		sendrequest(localaddr, userserver, soaquery, &request);
2473143731Sdougb	else
2474193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2475143731Sdougb	goto out;
2476135446Strhodes}
2477135446Strhodes
2478135446Strhodesstatic void
2479135446Strhodessendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2480135446Strhodes	    dns_message_t *msg, dns_request_t **request)
2481135446Strhodes{
2482135446Strhodes	isc_result_t result;
2483135446Strhodes	nsu_requestinfo_t *reqinfo;
2484135446Strhodes
2485135446Strhodes	reqinfo = isc_mem_get(mctx, sizeof(nsu_requestinfo_t));
2486135446Strhodes	if (reqinfo == NULL)
2487135446Strhodes		fatal("out of memory");
2488135446Strhodes	reqinfo->msg = msg;
2489135446Strhodes	reqinfo->addr = destaddr;
2490135446Strhodes	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr, 0,
2491135446Strhodes					(userserver != NULL) ? tsigkey : NULL,
2492135446Strhodes					FIND_TIMEOUT * 20, FIND_TIMEOUT, 3,
2493135446Strhodes					global_task, recvsoa, reqinfo, request);
2494135446Strhodes	check_result(result, "dns_request_createvia");
2495135446Strhodes	requests++;
2496135446Strhodes}
2497135446Strhodes
2498193149Sdougb#ifdef GSSAPI
2499224092Sdougb
2500224092Sdougb/*
2501224092Sdougb * Get the realm from the users kerberos ticket if possible
2502224092Sdougb */
2503135446Strhodesstatic void
2504224092Sdougbget_ticket_realm(isc_mem_t *mctx)
2505193149Sdougb{
2506224092Sdougb	krb5_context ctx;
2507224092Sdougb	krb5_error_code rc;
2508224092Sdougb	krb5_ccache ccache;
2509224092Sdougb	krb5_principal princ;
2510224092Sdougb	char *name, *ticket_realm;
2511224092Sdougb
2512224092Sdougb	rc = krb5_init_context(&ctx);
2513224092Sdougb	if (rc != 0)
2514224092Sdougb		return;
2515224092Sdougb
2516224092Sdougb	rc = krb5_cc_default(ctx, &ccache);
2517224092Sdougb	if (rc != 0) {
2518224092Sdougb		krb5_free_context(ctx);
2519224092Sdougb		return;
2520224092Sdougb	}
2521224092Sdougb
2522224092Sdougb	rc = krb5_cc_get_principal(ctx, ccache, &princ);
2523224092Sdougb	if (rc != 0) {
2524224092Sdougb		krb5_cc_close(ctx, ccache);
2525224092Sdougb		krb5_free_context(ctx);
2526224092Sdougb		return;
2527224092Sdougb	}
2528224092Sdougb
2529224092Sdougb	rc = krb5_unparse_name(ctx, princ, &name);
2530224092Sdougb	if (rc != 0) {
2531224092Sdougb		krb5_free_principal(ctx, princ);
2532224092Sdougb		krb5_cc_close(ctx, ccache);
2533224092Sdougb		krb5_free_context(ctx);
2534224092Sdougb		return;
2535224092Sdougb	}
2536224092Sdougb
2537224092Sdougb	ticket_realm = strrchr(name, '@');
2538224092Sdougb	if (ticket_realm != NULL) {
2539224092Sdougb		realm = isc_mem_strdup(mctx, ticket_realm);
2540224092Sdougb	}
2541224092Sdougb
2542224092Sdougb	free(name);
2543224092Sdougb	krb5_free_principal(ctx, princ);
2544224092Sdougb	krb5_cc_close(ctx, ccache);
2545224092Sdougb	krb5_free_context(ctx);
2546224092Sdougb	if (realm != NULL && debugging)
2547224092Sdougb		fprintf(stderr, "Found realm from ticket: %s\n", realm+1);
2548224092Sdougb}
2549224092Sdougb
2550224092Sdougb
2551224092Sdougbstatic void
2552224092Sdougbstart_gssrequest(dns_name_t *master) {
2553193149Sdougb	gss_ctx_id_t context;
2554193149Sdougb	isc_buffer_t buf;
2555193149Sdougb	isc_result_t result;
2556193149Sdougb	isc_uint32_t val = 0;
2557193149Sdougb	dns_message_t *rmsg;
2558193149Sdougb	dns_request_t *request = NULL;
2559193149Sdougb	dns_name_t *servname;
2560193149Sdougb	dns_fixedname_t fname;
2561193149Sdougb	char namestr[DNS_NAME_FORMATSIZE];
2562193149Sdougb	char keystr[DNS_NAME_FORMATSIZE];
2563224092Sdougb	char *err_message = NULL;
2564193149Sdougb
2565193149Sdougb	debug("start_gssrequest");
2566193149Sdougb	usevc = ISC_TRUE;
2567193149Sdougb
2568193149Sdougb	if (gssring != NULL)
2569224092Sdougb		dns_tsigkeyring_detach(&gssring);
2570193149Sdougb	gssring = NULL;
2571193149Sdougb	result = dns_tsigkeyring_create(mctx, &gssring);
2572193149Sdougb
2573193149Sdougb	if (result != ISC_R_SUCCESS)
2574193149Sdougb		fatal("dns_tsigkeyring_create failed: %s",
2575193149Sdougb		      isc_result_totext(result));
2576193149Sdougb
2577193149Sdougb	dns_name_format(master, namestr, sizeof(namestr));
2578193149Sdougb	if (kserver == NULL) {
2579193149Sdougb		kserver = isc_mem_get(mctx, sizeof(isc_sockaddr_t));
2580193149Sdougb		if (kserver == NULL)
2581193149Sdougb			fatal("out of memory");
2582193149Sdougb	}
2583193149Sdougb	if (userserver == NULL)
2584224092Sdougb		get_address(namestr, dnsport, kserver);
2585193149Sdougb	else
2586262706Serwin		(void)memmove(kserver, userserver, sizeof(isc_sockaddr_t));
2587193149Sdougb
2588193149Sdougb	dns_fixedname_init(&fname);
2589193149Sdougb	servname = dns_fixedname_name(&fname);
2590193149Sdougb
2591224092Sdougb	if (realm == NULL)
2592224092Sdougb		get_ticket_realm(mctx);
2593224092Sdougb
2594193149Sdougb	result = isc_string_printf(servicename, sizeof(servicename),
2595218384Sdougb				   "DNS/%s%s", namestr, realm ? realm : "");
2596193149Sdougb	if (result != ISC_R_SUCCESS)
2597193149Sdougb		fatal("isc_string_printf(servicename) failed: %s",
2598193149Sdougb		      isc_result_totext(result));
2599193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2600193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2601224092Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL);
2602193149Sdougb	if (result != ISC_R_SUCCESS)
2603193149Sdougb		fatal("dns_name_fromtext(servname) failed: %s",
2604193149Sdougb		      isc_result_totext(result));
2605193149Sdougb
2606193149Sdougb	dns_fixedname_init(&fkname);
2607193149Sdougb	keyname = dns_fixedname_name(&fkname);
2608193149Sdougb
2609193149Sdougb	isc_random_get(&val);
2610193149Sdougb	result = isc_string_printf(keystr, sizeof(keystr), "%u.sig-%s",
2611193149Sdougb				   val, namestr);
2612193149Sdougb	if (result != ISC_R_SUCCESS)
2613193149Sdougb		fatal("isc_string_printf(keystr) failed: %s",
2614193149Sdougb		      isc_result_totext(result));
2615193149Sdougb	isc_buffer_init(&buf, keystr, strlen(keystr));
2616193149Sdougb	isc_buffer_add(&buf, strlen(keystr));
2617193149Sdougb
2618224092Sdougb	result = dns_name_fromtext(keyname, &buf, dns_rootname, 0, NULL);
2619193149Sdougb	if (result != ISC_R_SUCCESS)
2620193149Sdougb		fatal("dns_name_fromtext(keyname) failed: %s",
2621193149Sdougb		      isc_result_totext(result));
2622193149Sdougb
2623193149Sdougb	/* Windows doesn't recognize name compression in the key name. */
2624193149Sdougb	keyname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
2625193149Sdougb
2626193149Sdougb	rmsg = NULL;
2627193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &rmsg);
2628193149Sdougb	if (result != ISC_R_SUCCESS)
2629193149Sdougb		fatal("dns_message_create failed: %s",
2630193149Sdougb		      isc_result_totext(result));
2631193149Sdougb
2632193149Sdougb	/* Build first request. */
2633193149Sdougb	context = GSS_C_NO_CONTEXT;
2634193149Sdougb	result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0,
2635224092Sdougb					&context, use_win2k_gsstsig,
2636224092Sdougb					mctx, &err_message);
2637193149Sdougb	if (result == ISC_R_FAILURE)
2638224092Sdougb		fatal("tkey query failed: %s",
2639224092Sdougb		      err_message != NULL ? err_message : "unknown error");
2640193149Sdougb	if (result != ISC_R_SUCCESS)
2641193149Sdougb		fatal("dns_tkey_buildgssquery failed: %s",
2642193149Sdougb		      isc_result_totext(result));
2643193149Sdougb
2644193149Sdougb	send_gssrequest(localaddr, kserver, rmsg, &request, context);
2645193149Sdougb}
2646193149Sdougb
2647193149Sdougbstatic void
2648193149Sdougbsend_gssrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
2649193149Sdougb		dns_message_t *msg, dns_request_t **request,
2650193149Sdougb		gss_ctx_id_t context)
2651193149Sdougb{
2652193149Sdougb	isc_result_t result;
2653193149Sdougb	nsu_gssinfo_t *reqinfo;
2654193149Sdougb	unsigned int options = 0;
2655193149Sdougb
2656193149Sdougb	debug("send_gssrequest");
2657193149Sdougb	reqinfo = isc_mem_get(mctx, sizeof(nsu_gssinfo_t));
2658193149Sdougb	if (reqinfo == NULL)
2659193149Sdougb		fatal("out of memory");
2660193149Sdougb	reqinfo->msg = msg;
2661193149Sdougb	reqinfo->addr = destaddr;
2662193149Sdougb	reqinfo->context = context;
2663193149Sdougb
2664193149Sdougb	options |= DNS_REQUESTOPT_TCP;
2665193149Sdougb	result = dns_request_createvia3(requestmgr, msg, srcaddr, destaddr,
2666193149Sdougb					options, tsigkey, FIND_TIMEOUT * 20,
2667193149Sdougb					FIND_TIMEOUT, 3, global_task, recvgss,
2668193149Sdougb					reqinfo, request);
2669193149Sdougb	check_result(result, "dns_request_createvia3");
2670193149Sdougb	if (debugging)
2671193149Sdougb		show_message(stdout, msg, "Outgoing update query:");
2672193149Sdougb	requests++;
2673193149Sdougb}
2674193149Sdougb
2675193149Sdougbstatic void
2676193149Sdougbrecvgss(isc_task_t *task, isc_event_t *event) {
2677193149Sdougb	dns_requestevent_t *reqev = NULL;
2678193149Sdougb	dns_request_t *request = NULL;
2679193149Sdougb	isc_result_t result, eresult;
2680193149Sdougb	dns_message_t *rcvmsg = NULL;
2681193149Sdougb	nsu_gssinfo_t *reqinfo;
2682193149Sdougb	dns_message_t *tsigquery = NULL;
2683193149Sdougb	isc_sockaddr_t *addr;
2684193149Sdougb	gss_ctx_id_t context;
2685193149Sdougb	isc_buffer_t buf;
2686193149Sdougb	dns_name_t *servname;
2687193149Sdougb	dns_fixedname_t fname;
2688224092Sdougb	char *err_message = NULL;
2689193149Sdougb
2690193149Sdougb	UNUSED(task);
2691193149Sdougb
2692193149Sdougb	ddebug("recvgss()");
2693193149Sdougb
2694193149Sdougb	requests--;
2695193149Sdougb
2696193149Sdougb	REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
2697193149Sdougb	reqev = (dns_requestevent_t *)event;
2698193149Sdougb	request = reqev->request;
2699193149Sdougb	eresult = reqev->result;
2700193149Sdougb	reqinfo = reqev->ev_arg;
2701193149Sdougb	tsigquery = reqinfo->msg;
2702193149Sdougb	context = reqinfo->context;
2703193149Sdougb	addr = reqinfo->addr;
2704193149Sdougb
2705193149Sdougb	if (shuttingdown) {
2706193149Sdougb		dns_request_destroy(&request);
2707193149Sdougb		dns_message_destroy(&tsigquery);
2708193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2709193149Sdougb		isc_event_free(&event);
2710193149Sdougb		maybeshutdown();
2711193149Sdougb		return;
2712193149Sdougb	}
2713193149Sdougb
2714193149Sdougb	if (eresult != ISC_R_SUCCESS) {
2715193149Sdougb		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2716193149Sdougb
2717193149Sdougb		isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
2718193149Sdougb		fprintf(stderr, "; Communication with %s failed: %s\n",
2719193149Sdougb			addrbuf, isc_result_totext(eresult));
2720193149Sdougb		if (userserver != NULL)
2721193149Sdougb			fatal("could not talk to specified name server");
2722193149Sdougb		else if (++ns_inuse >= lwconf->nsnext)
2723193149Sdougb			fatal("could not talk to any default name server");
2724193149Sdougb		ddebug("Destroying request [%p]", request);
2725193149Sdougb		dns_request_destroy(&request);
2726193149Sdougb		dns_message_renderreset(tsigquery);
2727193149Sdougb		sendrequest(localaddr, &servers[ns_inuse], tsigquery,
2728193149Sdougb			    &request);
2729193149Sdougb		isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2730193149Sdougb		isc_event_free(&event);
2731193149Sdougb		return;
2732193149Sdougb	}
2733193149Sdougb	isc_mem_put(mctx, reqinfo, sizeof(nsu_gssinfo_t));
2734193149Sdougb
2735193149Sdougb	isc_event_free(&event);
2736193149Sdougb	reqev = NULL;
2737193149Sdougb
2738193149Sdougb	ddebug("recvgss creating rcvmsg");
2739193149Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg);
2740193149Sdougb	check_result(result, "dns_message_create");
2741193149Sdougb
2742193149Sdougb	result = dns_request_getresponse(request, rcvmsg,
2743193149Sdougb					 DNS_MESSAGEPARSE_PRESERVEORDER);
2744193149Sdougb	check_result(result, "dns_request_getresponse");
2745193149Sdougb
2746193149Sdougb	if (debugging)
2747193149Sdougb		show_message(stderr, rcvmsg,
2748193149Sdougb			     "recvmsg reply from GSS-TSIG query");
2749193149Sdougb
2750193149Sdougb	if (rcvmsg->rcode == dns_rcode_formerr && !tried_other_gsstsig) {
2751193149Sdougb		ddebug("recvgss trying %s GSS-TSIG",
2752193149Sdougb		       use_win2k_gsstsig ? "Standard" : "Win2k");
2753193149Sdougb		if (use_win2k_gsstsig)
2754193149Sdougb			use_win2k_gsstsig = ISC_FALSE;
2755193149Sdougb		else
2756193149Sdougb			use_win2k_gsstsig = ISC_TRUE;
2757193149Sdougb		tried_other_gsstsig = ISC_TRUE;
2758193149Sdougb		start_gssrequest(&restart_master);
2759193149Sdougb		goto done;
2760193149Sdougb	}
2761193149Sdougb
2762193149Sdougb	if (rcvmsg->rcode != dns_rcode_noerror &&
2763193149Sdougb	    rcvmsg->rcode != dns_rcode_nxdomain)
2764193149Sdougb		fatal("response to GSS-TSIG query was unsuccessful");
2765193149Sdougb
2766193149Sdougb
2767193149Sdougb	dns_fixedname_init(&fname);
2768193149Sdougb	servname = dns_fixedname_name(&fname);
2769193149Sdougb	isc_buffer_init(&buf, servicename, strlen(servicename));
2770193149Sdougb	isc_buffer_add(&buf, strlen(servicename));
2771224092Sdougb	result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL);
2772193149Sdougb	check_result(result, "dns_name_fromtext");
2773193149Sdougb
2774193149Sdougb	tsigkey = NULL;
2775193149Sdougb	result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname,
2776193149Sdougb				       &context, &tsigkey, gssring,
2777224092Sdougb				       use_win2k_gsstsig,
2778224092Sdougb				       &err_message);
2779193149Sdougb	switch (result) {
2780193149Sdougb
2781193149Sdougb	case DNS_R_CONTINUE:
2782193149Sdougb		send_gssrequest(localaddr, kserver, tsigquery, &request,
2783193149Sdougb				context);
2784193149Sdougb		break;
2785193149Sdougb
2786193149Sdougb	case ISC_R_SUCCESS:
2787193149Sdougb		/*
2788193149Sdougb		 * XXXSRA Waaay too much fun here.  There's no good
2789193149Sdougb		 * reason why we need a TSIG here (the people who put
2790193149Sdougb		 * it into the spec admitted at the time that it was
2791193149Sdougb		 * not a security issue), and Windows clients don't
2792193149Sdougb		 * seem to work if named complies with the spec and
2793193149Sdougb		 * includes the gratuitous TSIG.  So we're in the
2794193149Sdougb		 * bizarre situation of having to choose between
2795193149Sdougb		 * complying with a useless requirement in the spec
2796193149Sdougb		 * and interoperating.  This is nuts.  If we can
2797193149Sdougb		 * confirm this behavior, we should ask the WG to
2798193149Sdougb		 * consider removing the requirement for the
2799193149Sdougb		 * gratuitous TSIG here.  For the moment, we ignore
2800193149Sdougb		 * the TSIG -- this too is a spec violation, but it's
2801193149Sdougb		 * the least insane thing to do.
2802193149Sdougb		 */
2803193149Sdougb#if 0
2804193149Sdougb		/*
2805193149Sdougb		 * Verify the signature.
2806193149Sdougb		 */
2807193149Sdougb		rcvmsg->state = DNS_SECTION_ANY;
2808193149Sdougb		dns_message_setquerytsig(rcvmsg, NULL);
2809193149Sdougb		result = dns_message_settsigkey(rcvmsg, tsigkey);
2810193149Sdougb		check_result(result, "dns_message_settsigkey");
2811193149Sdougb		result = dns_message_checksig(rcvmsg, NULL);
2812193149Sdougb		ddebug("tsig verification: %s", dns_result_totext(result));
2813193149Sdougb		check_result(result, "dns_message_checksig");
2814193149Sdougb#endif /* 0 */
2815193149Sdougb
2816193149Sdougb		send_update(&tmpzonename, serveraddr, localaddr);
2817193149Sdougb		setzoneclass(dns_rdataclass_none);
2818193149Sdougb		break;
2819193149Sdougb
2820193149Sdougb	default:
2821224092Sdougb		fatal("dns_tkey_negotiategss: %s %s",
2822224092Sdougb		      isc_result_totext(result),
2823224092Sdougb		      err_message != NULL ? err_message : "");
2824193149Sdougb	}
2825193149Sdougb
2826193149Sdougb done:
2827193149Sdougb	dns_request_destroy(&request);
2828193149Sdougb	dns_message_destroy(&tsigquery);
2829193149Sdougb
2830193149Sdougb	dns_message_destroy(&rcvmsg);
2831193149Sdougb	ddebug("Out of recvgss");
2832193149Sdougb}
2833193149Sdougb#endif
2834193149Sdougb
2835193149Sdougbstatic void
2836135446Strhodesstart_update(void) {
2837135446Strhodes	isc_result_t result;
2838135446Strhodes	dns_rdataset_t *rdataset = NULL;
2839135446Strhodes	dns_name_t *name = NULL;
2840135446Strhodes	dns_request_t *request = NULL;
2841135446Strhodes	dns_message_t *soaquery = NULL;
2842135446Strhodes	dns_name_t *firstname;
2843135446Strhodes	dns_section_t section = DNS_SECTION_UPDATE;
2844135446Strhodes
2845135446Strhodes	ddebug("start_update()");
2846135446Strhodes
2847135446Strhodes	if (answer != NULL)
2848135446Strhodes		dns_message_destroy(&answer);
2849135446Strhodes
2850193149Sdougb	if (userzone != NULL && userserver != NULL && ! usegsstsig) {
2851135446Strhodes		send_update(userzone, userserver, localaddr);
2852135446Strhodes		setzoneclass(dns_rdataclass_none);
2853135446Strhodes		return;
2854135446Strhodes	}
2855135446Strhodes
2856135446Strhodes	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
2857135446Strhodes				    &soaquery);
2858135446Strhodes	check_result(result, "dns_message_create");
2859135446Strhodes
2860170222Sdougb	if (userserver == NULL)
2861170222Sdougb		soaquery->flags |= DNS_MESSAGEFLAG_RD;
2862135446Strhodes
2863135446Strhodes	result = dns_message_gettempname(soaquery, &name);
2864135446Strhodes	check_result(result, "dns_message_gettempname");
2865135446Strhodes
2866135446Strhodes	result = dns_message_gettemprdataset(soaquery, &rdataset);
2867135446Strhodes	check_result(result, "dns_message_gettemprdataset");
2868135446Strhodes
2869135446Strhodes	dns_rdataset_makequestion(rdataset, getzoneclass(), dns_rdatatype_soa);
2870135446Strhodes
2871170222Sdougb	if (userzone != NULL) {
2872170222Sdougb		dns_name_init(name, NULL);
2873170222Sdougb		dns_name_clone(userzone, name);
2874170222Sdougb	} else {
2875218384Sdougb		dns_rdataset_t *tmprdataset;
2876170222Sdougb		result = dns_message_firstname(updatemsg, section);
2877170222Sdougb		if (result == ISC_R_NOMORE) {
2878170222Sdougb			section = DNS_SECTION_PREREQUISITE;
2879170222Sdougb			result = dns_message_firstname(updatemsg, section);
2880170222Sdougb		}
2881170222Sdougb		if (result != ISC_R_SUCCESS) {
2882174187Sdougb			dns_message_puttempname(soaquery, &name);
2883174187Sdougb			dns_rdataset_disassociate(rdataset);
2884174187Sdougb			dns_message_puttemprdataset(soaquery, &rdataset);
2885174187Sdougb			dns_message_destroy(&soaquery);
2886170222Sdougb			done_update();
2887170222Sdougb			return;
2888170222Sdougb		}
2889170222Sdougb		firstname = NULL;
2890170222Sdougb		dns_message_currentname(updatemsg, section, &firstname);
2891170222Sdougb		dns_name_init(name, NULL);
2892170222Sdougb		dns_name_clone(firstname, name);
2893218384Sdougb		/*
2894218384Sdougb		 * Looks to see if the first name references a DS record
2895218384Sdougb		 * and if that name is not the root remove a label as DS
2896218384Sdougb		 * records live in the parent zone so we need to start our
2897218384Sdougb		 * search one label up.
2898218384Sdougb		 */
2899218384Sdougb		tmprdataset = ISC_LIST_HEAD(firstname->list);
2900218384Sdougb		if (section == DNS_SECTION_UPDATE &&
2901218384Sdougb		    !dns_name_equal(firstname, dns_rootname) &&
2902218384Sdougb		    tmprdataset->type == dns_rdatatype_ds) {
2903218384Sdougb		    unsigned int labels = dns_name_countlabels(name);
2904218384Sdougb		    dns_name_getlabelsequence(name, 1, labels - 1, name);
2905218384Sdougb		}
2906170222Sdougb	}
2907135446Strhodes
2908135446Strhodes	ISC_LIST_INIT(name->list);
2909135446Strhodes	ISC_LIST_APPEND(name->list, rdataset, link);
2910135446Strhodes	dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
2911135446Strhodes
2912135446Strhodes	if (userserver != NULL)
2913135446Strhodes		sendrequest(localaddr, userserver, soaquery, &request);
2914135446Strhodes	else {
2915135446Strhodes		ns_inuse = 0;
2916135446Strhodes		sendrequest(localaddr, &servers[ns_inuse], soaquery, &request);
2917135446Strhodes	}
2918135446Strhodes}
2919135446Strhodes
2920135446Strhodesstatic void
2921135446Strhodescleanup(void) {
2922135446Strhodes	ddebug("cleanup()");
2923135446Strhodes
2924135446Strhodes	if (answer != NULL)
2925135446Strhodes		dns_message_destroy(&answer);
2926193149Sdougb
2927193149Sdougb#ifdef GSSAPI
2928193149Sdougb	if (tsigkey != NULL) {
2929193149Sdougb		ddebug("detach tsigkey x%p", tsigkey);
2930193149Sdougb		dns_tsigkey_detach(&tsigkey);
2931193149Sdougb	}
2932193149Sdougb	if (gssring != NULL) {
2933224092Sdougb		ddebug("Detaching GSS-TSIG keyring");
2934224092Sdougb		dns_tsigkeyring_detach(&gssring);
2935193149Sdougb	}
2936193149Sdougb	if (kserver != NULL) {
2937193149Sdougb		isc_mem_put(mctx, kserver, sizeof(isc_sockaddr_t));
2938193149Sdougb		kserver = NULL;
2939193149Sdougb	}
2940218384Sdougb	if (realm != NULL) {
2941218384Sdougb		isc_mem_free(mctx, realm);
2942218384Sdougb		realm = NULL;
2943218384Sdougb	}
2944193149Sdougb#endif
2945193149Sdougb
2946225361Sdougb	if (sig0key != NULL)
2947225361Sdougb		dst_key_free(&sig0key);
2948225361Sdougb
2949135446Strhodes	ddebug("Shutting down task manager");
2950135446Strhodes	isc_taskmgr_destroy(&taskmgr);
2951135446Strhodes
2952135446Strhodes	ddebug("Destroying event");
2953135446Strhodes	isc_event_free(&global_event);
2954135446Strhodes
2955135446Strhodes	ddebug("Shutting down socket manager");
2956135446Strhodes	isc_socketmgr_destroy(&socketmgr);
2957135446Strhodes
2958135446Strhodes	ddebug("Shutting down timer manager");
2959135446Strhodes	isc_timermgr_destroy(&timermgr);
2960135446Strhodes
2961135446Strhodes	ddebug("Destroying hash context");
2962135446Strhodes	isc_hash_destroy();
2963135446Strhodes
2964170222Sdougb	ddebug("Destroying name state");
2965170222Sdougb	dns_name_destroy();
2966170222Sdougb
2967193149Sdougb	ddebug("Removing log context");
2968193149Sdougb	isc_log_destroy(&lctx);
2969193149Sdougb
2970135446Strhodes	ddebug("Destroying memory context");
2971135446Strhodes	if (memdebugging)
2972135446Strhodes		isc_mem_stats(mctx, stderr);
2973135446Strhodes	isc_mem_destroy(&mctx);
2974135446Strhodes}
2975135446Strhodes
2976135446Strhodesstatic void
2977135446Strhodesgetinput(isc_task_t *task, isc_event_t *event) {
2978135446Strhodes	isc_boolean_t more;
2979135446Strhodes
2980135446Strhodes	UNUSED(task);
2981135446Strhodes
2982135446Strhodes	if (shuttingdown) {
2983135446Strhodes		maybeshutdown();
2984135446Strhodes		return;
2985135446Strhodes	}
2986135446Strhodes
2987135446Strhodes	if (global_event == NULL)
2988135446Strhodes		global_event = event;
2989135446Strhodes
2990135446Strhodes	reset_system();
2991135446Strhodes	more = user_interaction();
2992135446Strhodes	if (!more) {
2993135446Strhodes		isc_app_shutdown();
2994135446Strhodes		return;
2995135446Strhodes	}
2996135446Strhodes	start_update();
2997135446Strhodes	return;
2998135446Strhodes}
2999135446Strhodes
3000135446Strhodesint
3001135446Strhodesmain(int argc, char **argv) {
3002135446Strhodes	isc_result_t result;
3003135446Strhodes	style = &dns_master_style_debug;
3004135446Strhodes
3005135446Strhodes	input = stdin;
3006135446Strhodes
3007135446Strhodes	interactive = ISC_TF(isatty(0));
3008135446Strhodes
3009135446Strhodes	isc_app_start();
3010135446Strhodes
3011193149Sdougb	pre_parse_args(argc, argv);
3012135446Strhodes
3013193149Sdougb	result = isc_mem_create(0, 0, &mctx);
3014193149Sdougb	check_result(result, "isc_mem_create");
3015193149Sdougb
3016193149Sdougb	parse_args(argc, argv, mctx, &entropy);
3017193149Sdougb
3018135446Strhodes	setup_system();
3019135446Strhodes
3020135446Strhodes	result = isc_app_onrun(mctx, global_task, getinput, NULL);
3021135446Strhodes	check_result(result, "isc_app_onrun");
3022135446Strhodes
3023135446Strhodes	(void)isc_app_run();
3024135446Strhodes
3025135446Strhodes	cleanup();
3026135446Strhodes
3027135446Strhodes	isc_app_finish();
3028135446Strhodes
3029135446Strhodes	if (seenerror)
3030135446Strhodes		return (2);
3031135446Strhodes	else
3032135446Strhodes		return (0);
3033135446Strhodes}
3034