1/*	$NetBSD: dns.c,v 1.5 2022/04/03 01:10:58 christos Exp $	*/
2
3/* dns.c
4
5   Domain Name Service subroutines. */
6
7/*
8 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 2001-2003 by Internet Software Consortium
10 *
11 * This Source Code Form is subject to the terms of the Mozilla Public
12 * License, v. 2.0. If a copy of the MPL was not distributed with this
13 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 *   Internet Systems Consortium, Inc.
24 *   PO Box 360
25 *   Newmarket, NH 03857 USA
26 *   <info@isc.org>
27 *   https://www.isc.org/
28 *
29 */
30
31#include <sys/cdefs.h>
32__RCSID("$NetBSD: dns.c,v 1.5 2022/04/03 01:10:58 christos Exp $");
33
34/*! \file common/dns.c
35 */
36#include "dhcpd.h"
37#include "arpa/nameser.h"
38#include <isc/md.h>
39#include <dns/result.h>
40
41/*
42 * This file contains code to connect the DHCP code to the libdns modules.
43 * As part of that function it maintains a database of zone cuts that can
44 * be used to figure out which server should be contacted to update any
45 * given domain name.  Included in the zone information may be a pointer
46 * to a key in which case that key is used for the update.  If no zone
47 * is found then the DNS code determines the zone on its own.
48 *
49 * The way this works is that you define the domain name to which an
50 * SOA corresponds, and the addresses of some primaries for that domain name:
51 *
52 *	zone FOO.COM {
53 *	  primary 10.0.17.1;
54 *	  secondary 10.0.22.1, 10.0.23.1;
55 *	  key "FOO.COM Key";
56 * 	}
57 *
58 * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
59 * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
60 * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
61 * looks for "FOO.COM", finds it. So it
62 * attempts the update to the primary for FOO.COM.   If that times out, it
63 * tries the secondaries.   You can list multiple primaries if you have some
64 * kind of magic name server that supports that.   You shouldn't list
65 * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
66 * support update forwarding, AFAIK).   If no TSIG key is listed, the update
67 * is attempted without TSIG.
68 *
69 * You can also include IPv6 addresses via the primary6 and secondary6
70 * options.  The search order for the addresses is primary, primary6,
71 * secondary and lastly secondary6, with a limit on the number of
72 * addresses used.  Currently this limit is 3.
73 *
74 * The DHCP server tries to find an existing zone for any given name by
75 * trying to look up a local zone structure for each domain containing
76 * that name, all the way up to '.'.   If it finds one cached, it tries
77 * to use that one to do the update.   That's why it tries to update
78 * "FOO.COM" above, even though theoretically it should try GAZANGA...
79 * and TOPANGA... first.
80 *
81 * If the update fails with a predefined zone the zone is marked as bad
82 * and another search of the predefined zones is done.  If no predefined
83 * zone is found finding a zone is left to the DNS module via examination
84 * of SOA records.  If the DNS module finds a zone it may cache the zone
85 * but the zone won't be cached here.
86 *
87 * TSIG updates are not performed on zones found by the DNS module - if
88 * you want TSIG updates you _must_ write a zone definition linking the
89 * key to the zone.   In cases where you know for sure what the key is
90 * but do not want to hardcode the IP addresses of the primary or
91 * secondaries, a zone declaration can be made that doesn't include any
92 * primary or secondary declarations.   When the DHCP server encounters
93 * this while hunting up a matching zone for a name, it looks up the SOA,
94 * fills in the IP addresses, and uses that record for the update.
95 * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
96 * discarded, TSIG key and all.   The search for the zone then continues
97 * as if the zone record hadn't been found.   Zones without IP addresses
98 * don't match when initially hunting for a zone to update.
99 *
100 * When an update is attempted and no predefined zone is found
101 * that matches any enclosing domain of the domain being updated, the DHCP
102 * server goes through the same process that is done when the update to a
103 * predefined zone fails - starting with the most specific domain
104 * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
105 * it tries to look up an SOA record.
106 *
107 * TSIG keys are defined like this:
108 *
109 *	key "FOO.COM Key" {
110 *		algorithm HMAC-MD5.SIG-ALG.REG.INT;
111 *		secret <Base64>;
112 *	}
113 *
114 * <Base64> is a number expressed in base64 that represents the key.
115 * It's also permissible to use a quoted string here - this will be
116 * translated as the ASCII bytes making up the string, and will not
117 * include any NUL termination.  The key name can be any text string,
118 * and the key type must be one of the key types defined in the draft
119 * or by the IANA.  Currently only the HMAC-MD5... key type is
120 * supported.
121 *
122 * The DDNS processing has been split into two areas.  One is the
123 * control code that determines what should be done.  That code is found
124 * in the client or server directories.  The other is the common code
125 * that performs functions such as properly formatting the arguments.
126 * That code is found in this file.  The basic processing flow for a
127 * DDNS update is:
128 * In the client or server code determine what needs to be done and
129 * collect the necesary information then pass it to a function from
130 * this file.
131 * In this code lookup the zone and extract the zone and key information
132 * (if available) and prepare the arguments for the DNS module.
133 * When the DNS module completes its work (times out or gets a reply)
134 * it will trigger another function here which does generic processing
135 * and then passes control back to the code from the server or client.
136 * The server or client code then determines the next step which may
137 * result in another call to this module in which case the process repeats.
138 */
139
140dns_zone_hash_t *dns_zone_hash;
141
142/*
143 * DHCP dns structures
144 * Normally the relationship between these structures isn't one to one
145 * but in the DHCP case it (mostly) is.  To make the allocations, frees,
146 * and passing of the memory easier we make a single structure with all
147 * the pieces.
148 *
149 * The maximum size of the data buffer should be large enough for any
150 * items DHCP will generate
151 */
152
153typedef struct dhcp_ddns_rdata {
154	dns_rdata_t	rdata;
155	dns_rdatalist_t rdatalist;
156	dns_rdataset_t  rdataset;
157} dhcp_ddns_data_t;
158
159/* Function pointer type for functions which build DDNS update contents */
160typedef isc_result_t (*builder_func_t)(dhcp_ddns_cb_t *ddns_cb,
161					dhcp_ddns_data_t *dataspace,
162					dns_name_t *pname, dns_name_t *uname);
163
164#if defined (NSUPDATE)
165#if defined (DNS_ZONE_LOOKUP)
166
167/*
168 * The structure used to find a nameserver if there wasn't a zone entry.
169 * Currently we assume we won't have many of these outstanding at any
170 * time so we go with a simple linked list.
171 * In use find_zone_start() will fill in the oname with the name
172 * requested by the DDNS code.  zname will point to it and be
173 * advanced as labels are removed.  If the DNS client code returns
174 * a set of name servers eventp and rdataset will be set.  Then
175 * the code will walk through the nameservers in namelist and
176 * find addresses that are stored in addrs and addrs6.
177 */
178
179typedef struct dhcp_ddns_ns {
180	struct dhcp_ddns_ns *next;
181	struct data_string oname;     /* the original name for DDNS */
182	char *zname;                  /* a pointer into the original name for
183					 the zone we are checking */
184	dns_clientresevent_t *eventp; /* pointer to the event that provided the
185					 namelist, we can't free the eventp
186					 until we free the namelist */
187	dns_name_t *ns_name;          /* current name server we are examining */
188	dns_rdataset_t *rdataset;
189	dns_rdatatype_t rdtype;       /* type of address we want */
190
191	struct in_addr addrs[DHCP_MAXNS];   /* space for v4 addresses */
192	struct in6_addr addrs6[DHCP_MAXNS]; /* space for v6 addresses */
193	int num_addrs;
194	int num_addrs6;
195	int ttl;
196
197	void *transaction;             /* transaction id for DNS calls */
198} dhcp_ddns_ns_t;
199
200/*
201 * The list of DDNS names for which we are attempting to find a name server.
202 * This list is used for finding the name server, it doesn't include the
203 * information necessary to do the DDNS request after finding a name server.
204 * The code attempts to minimize duplicate requests by examining the list
205 * to see if we are already trying to find a substring of the new request.
206 * For example imagine the first request is "a.b.c.d.e." and the server has
207 * already discarded the first two lables and is trying "c.d.e.".  If the
208 * next request is for "x.y.c.d.e." the code assumes the in progress
209 * request is sufficient and doesn't add a new request for the second name.
210 * If the next request was for "x.y.z.d.e." the code doesn't assume they
211 * will use the same nameserver and starts a second request.
212 * This strategy will not eliminate all duplicates but is simple and
213 * should be sufficient.
214 */
215dhcp_ddns_ns_t *dns_outstanding_ns = NULL;
216
217/*
218 * Routines to manipulate the list of outstanding searches
219 *
220 * add_to_ns_queue() - adds the given control block to the queue
221 *
222 * remove_from_ns_queue() - removes the given control block from
223 * the queue
224 *
225 * find_in_ns_queue() compares the name from the given control
226 * block with the control blocks in the queue.  It returns
227 * success if a matching entry is found.  In order to match
228 * the entry already on the queue must be shorter than the
229 * incoming name must match the ending substring of the name.
230 */
231
232static void
233add_to_ns_queue(dhcp_ddns_ns_t *ns_cb)
234{
235	ns_cb->next = dns_outstanding_ns;
236	dns_outstanding_ns = ns_cb;
237}
238
239
240static void
241remove_from_ns_queue(dhcp_ddns_ns_t *ns_cb)
242{
243	dhcp_ddns_ns_t **foo;
244
245	foo = &dns_outstanding_ns;
246	while (*foo) {
247		if (*foo == ns_cb) {
248			*foo = ns_cb->next;
249			break;
250		}
251		foo = &((*foo)->next);
252	}
253	ns_cb->next = NULL;
254}
255
256static isc_result_t
257find_in_ns_queue(dhcp_ddns_ns_t *ns_cb)
258{
259	dhcp_ddns_ns_t *temp_cb;
260	int in_len, temp_len;
261
262	in_len = strlen(ns_cb->zname);
263
264	for(temp_cb = dns_outstanding_ns;
265	    temp_cb != NULL;
266	    temp_cb = temp_cb->next) {
267		temp_len = strlen(temp_cb->zname);
268		if (temp_len > in_len)
269			continue;
270		if (strcmp(temp_cb->zname,
271			   ns_cb->zname + (in_len - temp_len)) == 0)
272			return(ISC_R_SUCCESS);
273	}
274	return(ISC_R_NOTFOUND);
275}
276
277void cache_found_zone (dhcp_ddns_ns_t *);
278#endif
279
280void ddns_interlude(isc_task_t *, isc_event_t *);
281
282#if defined (TRACING)
283/*
284 * Code to support tracing DDNS packets.  We trace packets going to and
285 * coming from the libdns code but don't try to track the packets
286 * exchanged between the libdns code and the dns server(s) it contacts.
287 *
288 * The code is split into two sets of routines
289 *  input refers to messages received from the dns module
290 *  output refers to messages sent to the dns module
291 * Currently there are three routines in each set
292 *  write is used to write information about the message to the trace file
293 *        this routine is called directly from the proper place in the code.
294 *  read is used to read information about a message from the trace file
295 *       this routine is called from the trace loop as it reads through
296 *       the file and is registered via the trace_type_register routine.
297 *       When playing back a trace file we shall absorb records of output
298 *       messages as part of processing the write function, therefore
299 *       any output messages we encounter are flagged as errors.
300 *  stop isn't currently used in this code but is needed for the register
301 *       routine.
302 *
303 * We pass a pointer to a control block to the dns module which it returns
304 * to use as part of the result.  As the pointer may vary between traces
305 * we need to map between those from the trace file and the new ones during
306 * playback.
307 *
308 * The mapping is complicated a little as a pointer could be 4 or 8 bytes
309 * long.  We treat the old pointer as an 8 byte quantity and pad and compare
310 * as necessary.
311 */
312
313/*
314 * Structure used to map old pointers to new pointers.
315 * Old pointers are 8 bytes long as we don't know if the trace was
316 * done on a 64 bit or 32 bit machine.
317 */
318#define TRACE_PTR_LEN 8
319
320typedef struct dhcp_ddns_map {
321	char  old_pointer[TRACE_PTR_LEN];
322	void *new_pointer;
323	struct dhcp_ddns_map *next;
324} dhcp_ddns_map_t;
325
326/* The starting point for the map structure */
327static dhcp_ddns_map_t *ddns_map;
328
329trace_type_t *trace_ddns_input;
330trace_type_t *trace_ddns_output;
331
332/*
333 * The data written to the trace file is:
334 * 32 bits result from dns
335 * 64 bits pointer of cb
336 */
337
338static void
339trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result)
340{
341	trace_iov_t iov[2];
342	u_int32_t old_result;
343	char old_pointer[TRACE_PTR_LEN];
344
345	old_result = htonl((u_int32_t)result);
346	memset(old_pointer, 0, TRACE_PTR_LEN);
347	memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb));
348
349	iov[0].len = sizeof(old_result);
350	iov[0].buf = (char *)&old_result;
351	iov[1].len = TRACE_PTR_LEN;
352	iov[1].buf = old_pointer;
353	trace_write_packet_iov(trace_ddns_input, 2, iov, MDL);
354}
355
356/*
357 * Process the result and pointer from the trace file.
358 * We use the pointer map to find the proper pointer for this instance.
359 * Then we need to construct an event to pass along to the interlude
360 * function.
361 */
362static void
363trace_ddns_input_read(trace_type_t *ttype, unsigned length,
364				  char *buf)
365{
366	u_int32_t old_result;
367	char old_pointer[TRACE_PTR_LEN];
368	dns_clientupdateevent_t *eventp;
369	void *new_pointer;
370	dhcp_ddns_map_t *ddns_map_ptr;
371
372	if (length < (sizeof(old_result) + TRACE_PTR_LEN)) {
373		log_error("trace_ddns_input_read: data too short");
374		return;
375	}
376
377	memcpy(&old_result, buf, sizeof(old_result));
378	memcpy(old_pointer, buf + sizeof(old_result), TRACE_PTR_LEN);
379
380	/* map the old pointer to a new pointer */
381	for (ddns_map_ptr = ddns_map;
382	     ddns_map_ptr != NULL;
383	     ddns_map_ptr = ddns_map_ptr->next) {
384		if ((ddns_map_ptr->new_pointer != NULL) &&
385		    memcmp(ddns_map_ptr->old_pointer,
386			   old_pointer, TRACE_PTR_LEN) == 0) {
387			new_pointer = ddns_map_ptr->new_pointer;
388			ddns_map_ptr->new_pointer = NULL;
389			memset(ddns_map_ptr->old_pointer, 0, TRACE_PTR_LEN);
390			break;
391		}
392	}
393	if (ddns_map_ptr == NULL) {
394		log_error("trace_dns_input_read: unable to map cb pointer");
395		return;
396	}
397
398	eventp = (dns_clientupdateevent_t *)
399		isc_event_allocate(dhcp_gbl_ctx.mctx,
400				   dhcp_gbl_ctx.task,
401				   0,
402				   ddns_interlude,
403				   new_pointer,
404				   sizeof(dns_clientupdateevent_t));
405	if (eventp == NULL) {
406		log_error("trace_ddns_input_read: unable to allocate event");
407		return;
408	}
409	eventp->result = ntohl(old_result);
410
411
412	ddns_interlude(dhcp_gbl_ctx.task, (isc_event_t *)eventp);
413
414	return;
415}
416
417static void
418trace_ddns_input_stop(trace_type_t *ttype)
419{
420}
421
422/*
423 * We use the same arguments as for the dns startupdate function to
424 * allows us to choose between the two via a macro.  If tracing isn't
425 * in use we simply call the dns function directly.
426 *
427 * If we are doing playback we read the next packet from the file
428 * and compare the type.  If it matches we extract the results and pointer
429 * from the trace file.  The results are returned to the caller as if
430 * they had called the dns routine.  The pointer is used to construct a
431 * map for when the "reply" is processed.
432 *
433 * The data written to trace file is:
434 * 32 bits result
435 * 64 bits pointer of cb (DDNS Control block)
436 * contents of cb
437 */
438
439static isc_result_t
440trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
441			dns_name_t *zonename, dns_namelist_t *prerequisites,
442			dns_namelist_t *updates, isc_sockaddrlist_t *servers,
443			dns_tsec_t *tsec, unsigned int options,
444			isc_task_t *task, isc_taskaction_t action, void *arg,
445			dns_clientupdatetrans_t **transp)
446{
447	isc_result_t result;
448	u_int32_t old_result;
449	char old_pointer[TRACE_PTR_LEN];
450	dhcp_ddns_map_t *ddns_map_ptr;
451
452	if (trace_playback() != 0) {
453		/* We are doing playback, extract the entry from the file */
454		unsigned buflen = 0;
455		char *inbuf = NULL;
456
457		result = trace_get_packet(&trace_ddns_output,
458					  &buflen, &inbuf);
459		if (result != ISC_R_SUCCESS) {
460			log_error("trace_ddns_output_write: no input found");
461			return (ISC_R_FAILURE);
462		}
463		if (buflen < (sizeof(old_result) + TRACE_PTR_LEN)) {
464			log_error("trace_ddns_output_write: data too short");
465			dfree(inbuf, MDL);
466			return (ISC_R_FAILURE);
467		}
468		memcpy(&old_result, inbuf, sizeof(old_result));
469		result = ntohl(old_result);
470		memcpy(old_pointer, inbuf + sizeof(old_result), TRACE_PTR_LEN);
471		dfree(inbuf, MDL);
472
473		/* add the pointer to the pointer map */
474		for (ddns_map_ptr = ddns_map;
475		     ddns_map_ptr != NULL;
476		     ddns_map_ptr = ddns_map_ptr->next) {
477			if (ddns_map_ptr->new_pointer == NULL) {
478				break;
479			}
480		}
481
482		/*
483		 * If we didn't find an empty entry, allocate an entry and
484		 * link it into the list.  The list isn't ordered.
485		 */
486		if (ddns_map_ptr == NULL) {
487			ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL);
488			if (ddns_map_ptr == NULL) {
489				log_error("trace_ddns_output_write: "
490					  "unable to allocate map entry");
491				return(ISC_R_FAILURE);
492				}
493			ddns_map_ptr->next = ddns_map;
494			ddns_map = ddns_map_ptr;
495		}
496
497		memcpy(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN);
498		ddns_map_ptr->new_pointer = arg;
499	}
500	else {
501		/* We aren't doing playback, make the actual call */
502		result = dns_client_startupdate(client, rdclass, zonename,
503						prerequisites, updates,
504						servers, tsec, options,
505						task, action, arg, transp);
506	}
507
508	if (trace_record() != 0) {
509		/* We are recording, save the information to the file */
510		trace_iov_t iov[3];
511		old_result = htonl((u_int32_t)result);
512		memset(old_pointer, 0, TRACE_PTR_LEN);
513		memcpy(old_pointer, &arg, sizeof(arg));
514		iov[0].len = sizeof(old_result);
515		iov[0].buf = (char *)&old_result;
516		iov[1].len = TRACE_PTR_LEN;
517		iov[1].buf = old_pointer;
518
519		/* Write out the entire cb, in case we want to look at it */
520		iov[2].len = sizeof(dhcp_ddns_cb_t);
521		iov[2].buf = (char *)arg;
522
523		trace_write_packet_iov(trace_ddns_output, 3, iov, MDL);
524	}
525
526	return(result);
527}
528
529static void
530trace_ddns_output_read(trace_type_t *ttype, unsigned length,
531				   char *buf)
532{
533	log_error("unaccounted for ddns output.");
534}
535
536static void
537trace_ddns_output_stop(trace_type_t *ttype)
538{
539}
540
541void
542trace_ddns_init()
543{
544	trace_ddns_output = trace_type_register("ddns-output", NULL,
545						trace_ddns_output_read,
546						trace_ddns_output_stop, MDL);
547	trace_ddns_input  = trace_type_register("ddns-input", NULL,
548						trace_ddns_input_read,
549						trace_ddns_input_stop, MDL);
550	ddns_map = NULL;
551}
552
553#define ddns_update trace_ddns_output_write
554#else
555#define ddns_update dns_client_startupdate
556#endif /* TRACING */
557
558#define zone_resolve dns_client_startresolve
559
560/*
561 * Code to allocate and free a dddns control block.  This block is used
562 * to pass and track the information associated with a DDNS update request.
563 */
564dhcp_ddns_cb_t *
565ddns_cb_alloc(const char *file, int line)
566{
567	dhcp_ddns_cb_t *ddns_cb;
568	int i;
569
570	ddns_cb = dmalloc(sizeof(*ddns_cb), file, line);
571	if (ddns_cb != NULL) {
572		ISC_LIST_INIT(ddns_cb->zone_server_list);
573		for (i = 0; i < DHCP_MAXNS; i++) {
574			ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
575		}
576	}
577
578#if defined (DEBUG_DNS_UPDATES)
579	log_info("%s(%d): Allocating ddns_cb=%p", file, line, ddns_cb);
580#endif
581
582	return(ddns_cb);
583}
584
585void
586ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
587{
588#if defined (DEBUG_DNS_UPDATES)
589	log_info("%s(%d): freeing ddns_cb=%p", file, line, ddns_cb);
590#endif
591
592  	data_string_forget(&ddns_cb->fwd_name, file, line);
593	data_string_forget(&ddns_cb->rev_name, file, line);
594	data_string_forget(&ddns_cb->dhcid, file, line);
595
596	if (ddns_cb->zone != NULL) {
597		forget_zone((struct dns_zone **)&ddns_cb->zone);
598	}
599
600	/* Should be freed by now, check just in case. */
601	if (ddns_cb->transaction != NULL) {
602		log_error("Impossible memory leak at %s:%d (attempt to free "
603			  "DDNS Control Block before transaction).", MDL);
604	}
605
606	/* Should be freed by now, check just in case. */
607	if (ddns_cb->fixed6_ia) {
608		log_error("Possible memory leak at %s:%d (attempt to free "
609			  "DDNS Control Block before fxed6_ia).", MDL);
610	}
611
612	dfree(ddns_cb, file, line);
613}
614
615void
616ddns_cb_forget_zone(dhcp_ddns_cb_t *ddns_cb)
617{
618	int i;
619
620	forget_zone(&ddns_cb->zone);
621	ddns_cb->zone_name[0] = 0;
622	ISC_LIST_INIT(ddns_cb->zone_server_list);
623	for (i = 0; i < DHCP_MAXNS; i++) {
624		ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
625	}
626}
627#endif
628
629static isc_result_t remove_dns_zone (struct dns_zone *zone)
630{
631	struct dns_zone *tz = NULL;
632
633	if (dns_zone_hash) {
634		dns_zone_hash_lookup(&tz, dns_zone_hash, zone->name, 0, MDL);
635		if (tz != NULL) {
636			dns_zone_hash_delete(dns_zone_hash, tz->name, 0, MDL);
637			dns_zone_dereference(&tz, MDL);
638		}
639	}
640
641	return (ISC_R_SUCCESS);
642}
643
644isc_result_t enter_dns_zone (struct dns_zone *zone)
645{
646	struct dns_zone *tz = (struct dns_zone *)0;
647
648	if (dns_zone_hash) {
649		dns_zone_hash_lookup (&tz,
650				      dns_zone_hash, zone -> name, 0, MDL);
651		if (tz == zone) {
652			dns_zone_dereference (&tz, MDL);
653			return ISC_R_SUCCESS;
654		}
655		if (tz) {
656			dns_zone_hash_delete (dns_zone_hash,
657					      zone -> name, 0, MDL);
658			dns_zone_dereference (&tz, MDL);
659		}
660	} else {
661		if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
662			return ISC_R_NOMEMORY;
663	}
664
665	dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
666	return ISC_R_SUCCESS;
667}
668
669isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
670{
671	int len;
672	char *tname = (char *)0;
673	isc_result_t status;
674
675	if (!dns_zone_hash)
676		return ISC_R_NOTFOUND;
677
678	len = strlen (name);
679	if (name [len - 1] != '.') {
680		tname = dmalloc ((unsigned)len + 2, MDL);
681		if (!tname)
682			return ISC_R_NOMEMORY;
683		strcpy (tname, name);
684		tname [len] = '.';
685		tname [len + 1] = 0;
686		name = tname;
687	}
688	if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
689		status = ISC_R_NOTFOUND;
690	else if ((*zone)->timeout && (*zone)->timeout < cur_time) {
691		dns_zone_hash_delete(dns_zone_hash, (*zone)->name, 0, MDL);
692		dns_zone_dereference(zone, MDL);
693		status = ISC_R_NOTFOUND;
694	} else
695		status = ISC_R_SUCCESS;
696
697	if (tname)
698		dfree (tname, MDL);
699	return status;
700}
701
702int dns_zone_dereference (ptr, file, line)
703	struct dns_zone **ptr;
704	const char *file;
705	int line;
706{
707	struct dns_zone *dns_zone;
708
709	if ((ptr == NULL) || (*ptr == NULL)) {
710		log_error("%s(%d): null pointer", file, line);
711#if defined (POINTER_DEBUG)
712		abort();
713#else
714		return (0);
715#endif
716	}
717
718	dns_zone = *ptr;
719	*ptr = NULL;
720	--dns_zone->refcnt;
721	rc_register(file, line, ptr, dns_zone, dns_zone->refcnt, 1, RC_MISC);
722	if (dns_zone->refcnt > 0)
723		return (1);
724
725	if (dns_zone->refcnt < 0) {
726		log_error("%s(%d): negative refcnt!", file, line);
727#if defined (DEBUG_RC_HISTORY)
728		dump_rc_history(dns_zone);
729#endif
730#if defined (POINTER_DEBUG)
731		abort();
732#else
733		return (0);
734#endif
735	}
736
737	if (dns_zone->name)
738		dfree(dns_zone->name, file, line);
739	if (dns_zone->key)
740		omapi_auth_key_dereference(&dns_zone->key, file, line);
741	if (dns_zone->primary)
742		option_cache_dereference(&dns_zone->primary, file, line);
743	if (dns_zone->secondary)
744		option_cache_dereference(&dns_zone->secondary, file, line);
745	if (dns_zone->primary6)
746		option_cache_dereference(&dns_zone->primary6, file, line);
747	if (dns_zone->secondary6)
748		option_cache_dereference(&dns_zone->secondary6, file, line);
749	dfree(dns_zone, file, line);
750	return (1);
751}
752
753#if defined (NSUPDATE)
754#if defined (DNS_ZONE_LOOKUP)
755
756/* Helper function to copy the address from an rdataset to
757 * the nameserver control block.  Mostly to avoid really long
758 * lines in the nested for loops
759 */
760static void
761zone_addr_to_ns(dhcp_ddns_ns_t *ns_cb,
762		dns_rdataset_t *rdataset)
763{
764	dns_rdata_t rdata;
765	dns_rdata_in_a_t a;
766	dns_rdata_in_aaaa_t aaaa;
767
768	dns_rdata_init(&rdata);
769	dns_rdataset_current(rdataset, &rdata);
770	switch (rdataset->type) {
771	case dns_rdatatype_a:
772		(void) dns_rdata_tostruct(&rdata, &a, NULL);
773		memcpy(&ns_cb->addrs[ns_cb->num_addrs], &a.in_addr, 4);
774		ns_cb->num_addrs++;
775		dns_rdata_freestruct(&a);
776		break;
777	case dns_rdatatype_aaaa:
778		(void) dns_rdata_tostruct(&rdata, &aaaa, NULL);
779		memcpy(&ns_cb->addrs6[ns_cb->num_addrs6], &aaaa.in6_addr, 16);
780		ns_cb->num_addrs6++;
781		dns_rdata_freestruct(&aaaa);
782		break;
783	default:
784		break;
785	}
786
787	if ((ns_cb->ttl == 0) || (ns_cb->ttl > rdataset->ttl))
788		ns_cb->ttl = rdataset->ttl;
789}
790
791/*
792 * The following three routines co-operate to find the addresses of
793 * the nameservers to use for a zone if we don't have a zone statement.
794 * We strongly suggest the use of a zone statement to avoid problmes
795 * and to allow for the use of TSIG and therefore better security, but
796 * include this functionality for those that don't want such statements.
797 *
798 * find_zone_start(ddns_cb, direction)
799 * This is the first of the routines, it is called from the rest of
800 * the ddns code when we have received a request for DDNS for a name
801 * and don't have a zone entry that would cover that name.  The name
802 * is in the ddns_cb as specified by the direction (forward or reverse).
803 * The start function pulls the name out and constructs the name server
804 * block then starts the process by calling the DNS client code.
805 *
806 * find_zone_ns(taskp, eventp)
807 * This is the second step of the process.  The DNS client code will
808 * call this when it has gotten a response or timed out.  If the response
809 * doesn't have a list of nameservers we remove another label from the
810 * zone name and try again.  If the response does include a list of
811 * nameservers we start walking through the list attempting to get
812 * addresses for the nameservers.
813 *
814 * find_zone_addrs(taskp, eventp)
815 * This is the third step of the process.  In find_zone_ns we got
816 * a list of nameserves and started walking through them.  This continues
817 * the walk and if we get back any addresses it adds them to our list.
818 * When we get enough addresses or run out of nameservers we construct
819 * a zone entry and insert it into the zone hash for the rest of the
820 * DDNS code to use.
821 */
822static void
823find_zone_addrs(isc_task_t *taskp,
824		isc_event_t *eventp)
825{
826	dns_clientresevent_t *ddns_event = (dns_clientresevent_t *)eventp;
827	dhcp_ddns_ns_t *ns_cb = (dhcp_ddns_ns_t *)eventp->ev_arg;
828	dns_name_t *ns_name = NULL;
829	dns_rdataset_t *rdataset;
830	isc_result_t result;
831	dns_name_t *name;
832	dns_rdata_t rdata = DNS_RDATA_INIT;
833	dns_rdata_ns_t ns;
834
835
836	/* the transaction is done, get rid of the tag */
837	dns_client_destroyrestrans(&ns_cb->transaction);
838
839	/* If we succeeded we try and extract the addresses, if we can
840	 * and we have enough we are done.  If we didn't succeed or
841	 * we don't have enough addresses afterwards we drop through
842	 * and try the next item on the list.
843	 */
844	if (ddns_event->result == ISC_R_SUCCESS) {
845
846		for (name = ISC_LIST_HEAD(ddns_event->answerlist);
847		     name != NULL;
848		     name = ISC_LIST_NEXT(name, link)) {
849
850			for (rdataset = ISC_LIST_HEAD(name->list);
851			     rdataset != NULL;
852			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
853
854				for (result = dns_rdataset_first(rdataset);
855				     result == ISC_R_SUCCESS;
856				     result = dns_rdataset_next(rdataset)) {
857
858					/* add address to cb */
859					zone_addr_to_ns(ns_cb, rdataset);
860
861					/* We are done if we have
862					 * enough addresses
863					 */
864					if (ns_cb->num_addrs +
865					    ns_cb->num_addrs6 >= DHCP_MAXNS)
866						goto done;
867				}
868			}
869		}
870	}
871
872	/* We need more addresses.
873	 * We restart the loop we were in before.
874	 */
875
876	for (ns_name = ns_cb->ns_name;
877	     ns_name != NULL;
878	     ns_name = ISC_LIST_NEXT(ns_name, link)) {
879
880		if (ns_name == ns_cb->ns_name) {
881			/* first time through, use saved state */
882			rdataset = ns_cb->rdataset;
883		} else {
884			rdataset = ISC_LIST_HEAD(ns_name->list);
885		}
886
887		for (;
888		     rdataset != NULL;
889		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
890
891			if (rdataset->type != dns_rdatatype_ns)
892				continue;
893			dns_rdata_init(&rdata);
894
895			if (rdataset == ns_cb->rdataset) {
896				/* first time through use the saved state */
897				if (ns_cb->rdtype == dns_rdatatype_a) {
898					ns_cb->rdtype = dns_rdatatype_aaaa;
899				} else {
900					ns_cb->rdtype = dns_rdatatype_a;
901					if (dns_rdataset_next(rdataset) !=
902					    ISC_R_SUCCESS)
903						continue;
904				}
905			} else {
906				if ((!dns_rdataset_isassociated(rdataset)) ||
907				    (dns_rdataset_first(rdataset) !=
908				     ISC_R_SUCCESS))
909					continue;
910			}
911
912			dns_rdataset_current(rdataset, &rdata);
913			if (dns_rdata_tostruct(&rdata, &ns, NULL) !=
914			    ISC_R_SUCCESS)
915				continue;
916
917			/* Save our current state */
918			ns_cb->ns_name = ns_name;
919			ns_cb->rdataset = rdataset;
920
921			/* And call out to DNS */
922			result = zone_resolve(dhcp_gbl_ctx.dnsclient, &ns.name,
923					      dns_rdataclass_in,
924					      ns_cb->rdtype,
925					      DNS_CLIENTRESOPT_NODNSSEC,
926					      dhcp_gbl_ctx.task,
927					      find_zone_addrs,
928					      (void *)ns_cb,
929					      &ns_cb->transaction);
930
931			/* do we need to clean this? */
932			dns_rdata_freestruct(&ns);
933
934			if (result == ISC_R_SUCCESS)
935				/* we have started the next step, cleanup
936				 * the structures associated with this call
937				 * but leave the cb for the next round
938				 */
939				goto cleanup;
940
941			log_error("find_zone_addrs: unable to continue "
942				  "resolve: %s %s",
943				  ns_cb->zname,
944				  isc_result_totext(result));
945
946			/* The call to start a resolve transaction failed,
947			 * should we try to continue with any other names?
948			 * For now let's not, but let's use whatever we
949			 * may already have.
950			 */
951			goto done;
952		}
953	}
954
955 done:
956	/* we've either gotten our max number of addresses or
957	 * run out of nameservers to try.  Convert the cb into
958	 * a zone and insert it into the zone hash.  Then
959	 * we need to clean up the saved state.
960	 */
961	if ((ns_cb->num_addrs != 0) ||
962	    (ns_cb->num_addrs6 != 0))
963		cache_found_zone(ns_cb);
964
965	dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
966				 &ns_cb->eventp->answerlist);
967	isc_event_free((isc_event_t **)&ns_cb->eventp);
968
969	remove_from_ns_queue(ns_cb);
970	data_string_forget(&ns_cb->oname, MDL);
971	dfree(ns_cb, MDL);
972
973 cleanup:
974	/* cleanup any of the new state information */
975
976	dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
977				 &ddns_event->answerlist);
978	isc_event_free(&eventp);
979
980	return;
981
982}
983
984/*
985 * Routine to continue the process of finding a nameserver via the DNS
986 * This is routine is called when we are still trying to get a list
987 * of nameservers to process.
988 */
989
990static void
991find_zone_ns(isc_task_t *taskp,
992	     isc_event_t *eventp)
993{
994	dns_clientresevent_t *ddns_event = (dns_clientresevent_t *)eventp;
995	dhcp_ddns_ns_t *ns_cb = (dhcp_ddns_ns_t *)eventp->ev_arg;
996	dns_fixedname_t zname0;
997	dns_name_t *zname = NULL, *ns_name = NULL;
998	dns_rdataset_t *rdataset;
999	isc_result_t result;
1000	dns_rdata_t rdata = DNS_RDATA_INIT;
1001	dns_rdata_ns_t ns;
1002
1003	/* the transaction is done, get rid of the tag */
1004	dns_client_destroyrestrans(&ns_cb->transaction);
1005
1006	if (ddns_event->result != ISC_R_SUCCESS) {
1007		/* We didn't find any nameservers, try again */
1008
1009		/* Remove a label and continue */
1010		ns_cb->zname = strchr(ns_cb->zname, '.');
1011		if ((ns_cb->zname == NULL) ||
1012		    (ns_cb->zname[1] == 0)) {
1013			/* No more labels, all done */
1014			goto cleanup;
1015		}
1016		ns_cb->zname++;
1017
1018		/* Create a DNS version of the zone name and call the
1019		 * resolver code */
1020		if (((result = dhcp_isc_name((unsigned char *)ns_cb->zname,
1021					     &zname0, &zname))
1022		     != ISC_R_SUCCESS) ||
1023		    ((result = zone_resolve(dhcp_gbl_ctx.dnsclient,
1024					    zname, dns_rdataclass_in,
1025					    dns_rdatatype_ns,
1026					    DNS_CLIENTRESOPT_NODNSSEC,
1027					    dhcp_gbl_ctx.task,
1028					    find_zone_ns,
1029					    (void *)ns_cb,
1030					    &ns_cb->transaction))
1031		     != ISC_R_SUCCESS)) {
1032			log_error("find_zone_ns: Unable to build "
1033				  "name or start resolve: %s %s",
1034				  ns_cb->zname,
1035				  isc_result_totext(result));
1036			goto cleanup;
1037		}
1038
1039		/* we have successfully started the next iteration
1040		 * of this step, clean up from the call and continue */
1041                dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
1042                                         &ddns_event->answerlist);
1043		isc_event_free(&eventp);
1044		return;
1045	}
1046
1047	/* We did get a set of nameservers, save the information and
1048	 * start trying to get addresses
1049	 */
1050	ns_cb->eventp = ddns_event;
1051	for (ns_name = ISC_LIST_HEAD(ddns_event->answerlist);
1052	     ns_name != NULL;
1053	     ns_name = ISC_LIST_NEXT(ns_name, link)) {
1054
1055		for (rdataset = ISC_LIST_HEAD(ns_name->list);
1056		     rdataset != NULL;
1057		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
1058
1059			if (rdataset->type != dns_rdatatype_ns)
1060				continue;
1061
1062			if ((!dns_rdataset_isassociated(rdataset)) ||
1063			    (dns_rdataset_first(rdataset) !=
1064			     ISC_R_SUCCESS))
1065				continue;
1066
1067			dns_rdataset_current(rdataset, &rdata);
1068			if (dns_rdata_tostruct(&rdata, &ns, NULL) !=
1069			    ISC_R_SUCCESS)
1070				continue;
1071
1072			/* Save our current state */
1073			ns_cb->ns_name = ns_name;
1074			ns_cb->rdataset = rdataset;
1075
1076			/* And call out to DNS */
1077			result = zone_resolve(dhcp_gbl_ctx.dnsclient, &ns.name,
1078					      dns_rdataclass_in,
1079					      ns_cb->rdtype,
1080					      DNS_CLIENTRESOPT_NODNSSEC,
1081					      dhcp_gbl_ctx.task,
1082					      find_zone_addrs,
1083					      (void *)ns_cb,
1084					      &ns_cb->transaction);
1085
1086			/* do we need to clean this? */
1087			dns_rdata_freestruct(&ns);
1088
1089			if (result == ISC_R_SUCCESS)
1090				/* We have successfully started the next step
1091				 * we don't cleanup the eventp block as we are
1092				 * still using it.
1093				 */
1094				return;
1095
1096			log_error("find_zone_ns: unable to continue "
1097				  "resolve: %s %s",
1098				  ns_cb->zname,
1099				  isc_result_totext(result));
1100
1101			/* The call to start a resolve transaction failed,
1102			 * should we try to continue with any other names?
1103			 * For now let's not
1104			 */
1105			goto cleanup;
1106		}
1107	}
1108
1109 cleanup:
1110	/* When we add a queue to manage the DDNS
1111	 * requests we will need to remove any that
1112	 * were waiting for this resolution */
1113
1114	dns_client_freeresanswer(dhcp_gbl_ctx.dnsclient,
1115				 &ddns_event->answerlist);
1116	isc_event_free(&eventp);
1117
1118	remove_from_ns_queue(ns_cb);
1119
1120	data_string_forget(&ns_cb->oname, MDL);
1121	dfree(ns_cb, MDL);
1122	return;
1123
1124}
1125
1126/*
1127 * Start the process of finding nameservers via the DNS because
1128 * we don't have a zone entry already.
1129 * We construct a control block and fill in the DDNS name.  As
1130 * the process continues we shall move the zname pointer to
1131 * indicate which labels we are still using.  The rest of
1132 * the control block will be filled in as we continue processing.
1133 */
1134static isc_result_t
1135find_zone_start(dhcp_ddns_cb_t *ddns_cb, int direction)
1136{
1137	isc_result_t status = ISC_R_NOTFOUND;
1138	dhcp_ddns_ns_t *ns_cb;
1139	dns_fixedname_t zname0;
1140	dns_name_t *zname = NULL;
1141
1142	/*
1143	 * We don't validate np as that was already done in find_cached_zone()
1144	 */
1145
1146	/* Allocate the control block for this request */
1147	ns_cb = dmalloc(sizeof(*ns_cb), MDL);
1148	if (ns_cb == NULL) {
1149		log_error("find_zone_start: unable to allocate cb");
1150		return(ISC_R_FAILURE);
1151	}
1152	ns_cb->rdtype = dns_rdatatype_a;
1153
1154	/* Copy the data string so the NS lookup is independent of the DDNS */
1155	if (direction == FIND_FORWARD) {
1156		data_string_copy(&ns_cb->oname,  &ddns_cb->fwd_name, MDL);
1157	} else {
1158		data_string_copy(&ns_cb->oname,  &ddns_cb->rev_name, MDL);
1159	}
1160	ns_cb->zname = (char *)ns_cb->oname.data;
1161
1162	/*
1163	 * Check the dns_outstanding_ns queue to see if we are
1164	 * already processing something that would cover this name
1165	 */
1166	if (find_in_ns_queue(ns_cb) == ISC_R_SUCCESS) {
1167		data_string_forget(&ns_cb->oname, MDL);
1168		dfree(ns_cb, MDL);
1169		return (ISC_R_SUCCESS);
1170	}
1171
1172	/* Create a DNS version of the zone name and call the
1173	 * resolver code */
1174	if (((status = dhcp_isc_name((unsigned char *)ns_cb->zname,
1175				     &zname0, &zname))
1176	     != ISC_R_SUCCESS) ||
1177	    ((status = zone_resolve(dhcp_gbl_ctx.dnsclient,
1178				    zname, dns_rdataclass_in,
1179				    dns_rdatatype_ns,
1180				    DNS_CLIENTRESOPT_NODNSSEC,
1181				    dhcp_gbl_ctx.task,
1182				    find_zone_ns,
1183				    (void *)ns_cb,
1184				    &ns_cb->transaction))
1185	     != ISC_R_SUCCESS)) {
1186		log_error("find_zone_start: Unable to build "
1187			  "name or start resolve: %s %s",
1188			  ns_cb->zname,
1189			  isc_result_totext(status));
1190
1191		/* We failed to start the process, clean up */
1192		data_string_forget(&ns_cb->oname, MDL);
1193		dfree(ns_cb, MDL);
1194	} else {
1195		/* We started the process, attach the control block
1196		 * to the queue */
1197		add_to_ns_queue(ns_cb);
1198	}
1199
1200	return (status);
1201}
1202#endif
1203
1204isc_result_t
1205find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
1206{
1207	isc_result_t status = ISC_R_NOTFOUND;
1208	const char *np;
1209	struct dns_zone *zone = NULL;
1210	struct data_string nsaddrs;
1211	struct in_addr zone_addr;
1212	struct in6_addr zone_addr6;
1213	int ix;
1214
1215	if (direction == FIND_FORWARD) {
1216		np = (const char *)ddns_cb->fwd_name.data;
1217	} else {
1218		np = (const char *)ddns_cb->rev_name.data;
1219	}
1220
1221	/* We can't look up a null zone. */
1222	if ((np == NULL) || (*np == '\0')) {
1223		return (DHCP_R_INVALIDARG);
1224	}
1225
1226	/*
1227	 * For each subzone, try to find a cached zone.
1228	 */
1229	for (;;) {
1230		status = dns_zone_lookup(&zone, np);
1231		if (status == ISC_R_SUCCESS)
1232			break;
1233
1234		np = strchr(np, '.');
1235		if (np == NULL)
1236			break;
1237		np++;
1238	}
1239
1240	if (status != ISC_R_SUCCESS)
1241		return (status);
1242
1243	/* Make sure the zone is valid, we've already gotten
1244	 * rid of expired dynamic zones.  Check to see if
1245	 * we repudiated this zone.  If so give up.
1246	 */
1247	if ((zone->flags & DNS_ZONE_INACTIVE) != 0) {
1248		dns_zone_dereference(&zone, MDL);
1249		return (ISC_R_FAILURE);
1250	}
1251
1252	/* Make sure the zone name will fit. */
1253	if (strlen(zone->name) >= sizeof(ddns_cb->zone_name)) {
1254		dns_zone_dereference(&zone, MDL);
1255		return (ISC_R_NOSPACE);
1256	}
1257	strcpy((char *)&ddns_cb->zone_name[0], zone->name);
1258
1259	memset (&nsaddrs, 0, sizeof nsaddrs);
1260	ix = 0;
1261
1262	if (zone->primary) {
1263		if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1264					  NULL, NULL, &global_scope,
1265					  zone->primary, MDL)) {
1266			int ip = 0;
1267			while (ix < DHCP_MAXNS) {
1268				if (ip + 4 > nsaddrs.len)
1269					break;
1270				memcpy(&zone_addr, &nsaddrs.data[ip], 4);
1271				isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
1272						    &zone_addr,
1273						    NS_DEFAULTPORT);
1274				ISC_LIST_APPEND(ddns_cb->zone_server_list,
1275						&ddns_cb->zone_addrs[ix],
1276						link);
1277				ip += 4;
1278				ix++;
1279			}
1280			data_string_forget(&nsaddrs, MDL);
1281		}
1282	}
1283
1284	if (zone->primary6) {
1285		if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1286					  NULL, NULL, &global_scope,
1287					  zone->primary6, MDL)) {
1288			int ip = 0;
1289			while (ix < DHCP_MAXNS) {
1290				if (ip + 16 > nsaddrs.len)
1291					break;
1292				memcpy(&zone_addr6, &nsaddrs.data[ip], 16);
1293				isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix],
1294						    &zone_addr6,
1295						    NS_DEFAULTPORT);
1296				ISC_LIST_APPEND(ddns_cb->zone_server_list,
1297						&ddns_cb->zone_addrs[ix],
1298						link);
1299				ip += 16;
1300				ix++;
1301			}
1302			data_string_forget(&nsaddrs, MDL);
1303		}
1304	}
1305
1306	if (zone->secondary) {
1307		if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1308					  NULL, NULL, &global_scope,
1309					  zone->secondary, MDL)) {
1310			int ip = 0;
1311			while (ix < DHCP_MAXNS) {
1312				if (ip + 4 > nsaddrs.len)
1313					break;
1314				memcpy(&zone_addr, &nsaddrs.data[ip], 4);
1315				isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
1316						    &zone_addr,
1317						    NS_DEFAULTPORT);
1318				ISC_LIST_APPEND(ddns_cb->zone_server_list,
1319						&ddns_cb->zone_addrs[ix],
1320						link);
1321				ip += 4;
1322				ix++;
1323			}
1324			data_string_forget (&nsaddrs, MDL);
1325		}
1326	}
1327
1328	if (zone->secondary6) {
1329		if (evaluate_option_cache(&nsaddrs, NULL, NULL, NULL,
1330					  NULL, NULL, &global_scope,
1331					  zone->secondary6, MDL)) {
1332			int ip = 0;
1333			while (ix < DHCP_MAXNS) {
1334				if (ip + 16 > nsaddrs.len)
1335					break;
1336				memcpy(&zone_addr6, &nsaddrs.data[ip], 16);
1337				isc_sockaddr_fromin6(&ddns_cb->zone_addrs[ix],
1338						    &zone_addr6,
1339						    NS_DEFAULTPORT);
1340				ISC_LIST_APPEND(ddns_cb->zone_server_list,
1341						&ddns_cb->zone_addrs[ix],
1342						link);
1343				ip += 16;
1344				ix++;
1345			}
1346			data_string_forget (&nsaddrs, MDL);
1347		}
1348	}
1349
1350	dns_zone_reference(&ddns_cb->zone, zone, MDL);
1351	dns_zone_dereference (&zone, MDL);
1352	return ISC_R_SUCCESS;
1353}
1354
1355void forget_zone (struct dns_zone **zone)
1356{
1357	dns_zone_dereference (zone, MDL);
1358}
1359
1360void repudiate_zone (struct dns_zone **zone)
1361{
1362	/* verify that we have a pointer at least */
1363	if ((zone == NULL) || (*zone == NULL)) {
1364		log_info("Null argument to repudiate zone");
1365		return;
1366	}
1367
1368	(*zone)->flags |= DNS_ZONE_INACTIVE;
1369	dns_zone_dereference(zone, MDL);
1370}
1371
1372#if defined (DNS_ZONE_LOOKUP)
1373void cache_found_zone(dhcp_ddns_ns_t *ns_cb)
1374{
1375	struct dns_zone *zone = NULL;
1376	int len, remove_zone = 0;
1377
1378	/* See if there's already such a zone. */
1379	if (dns_zone_lookup(&zone, ns_cb->zname) == ISC_R_SUCCESS) {
1380		/* If it's not a dynamic zone, leave it alone. */
1381		if (zone->timeout == 0) {
1382			goto cleanup;
1383		}
1384
1385		/* Remove any old addresses in case they've changed */
1386		if (zone->primary)
1387			option_cache_dereference(&zone->primary, MDL);
1388		if (zone->primary6)
1389			option_cache_dereference(&zone->primary6, MDL);
1390
1391		/* Set the flag to remove the zone from the hash if
1392		   we have problems */
1393		remove_zone = 1;
1394	} else if (dns_zone_allocate(&zone, MDL) == 0) {
1395		return;
1396	} else {
1397		/* We've just allocated the zone, now we need
1398		 * to allocate space for the name and addresses
1399		 */
1400
1401		/* allocate space for the name */
1402		len = strlen(ns_cb->zname);
1403		zone->name = dmalloc(len + 2, MDL);
1404		if (zone->name == NULL) {
1405			goto cleanup;
1406		}
1407
1408		/* Copy the name and add a trailing '.' if necessary */
1409		strcpy(zone->name, ns_cb->zname);
1410		if (zone->name[len-1] != '.') {
1411			zone->name[len] = '.';
1412			zone->name[len+1] = 0;
1413		}
1414	}
1415
1416	zone->timeout = cur_time + ns_cb->ttl;
1417
1418	if (ns_cb->num_addrs != 0) {
1419		len = ns_cb->num_addrs * sizeof(struct in_addr);
1420		if ((!option_cache_allocate(&zone->primary, MDL)) ||
1421		    (!buffer_allocate(&zone->primary->data.buffer,
1422				      len, MDL))) {
1423			if (remove_zone == 1)
1424				remove_dns_zone(zone);
1425			goto cleanup;
1426		}
1427		memcpy(zone->primary->data.buffer->data, ns_cb->addrs, len);
1428		zone->primary->data.data =
1429			&zone->primary->data.buffer->data[0];
1430		zone->primary->data.len = len;
1431	}
1432	if (ns_cb->num_addrs6 != 0) {
1433		len = ns_cb->num_addrs6 * sizeof(struct in6_addr);
1434		if ((!option_cache_allocate(&zone->primary6, MDL)) ||
1435		    (!buffer_allocate(&zone->primary6->data.buffer,
1436				      len, MDL))) {
1437			if (remove_zone == 1)
1438				remove_dns_zone(zone);
1439			goto cleanup;
1440		}
1441		memcpy(zone->primary6->data.buffer->data, ns_cb->addrs6, len);
1442		zone->primary6->data.data =
1443			&zone->primary6->data.buffer->data[0];
1444		zone->primary6->data.len = len;
1445	}
1446
1447	enter_dns_zone(zone);
1448
1449 cleanup:
1450	dns_zone_dereference(&zone, MDL);
1451	return;
1452}
1453#endif
1454
1455/*!
1456 * \brief Create an id for a client
1457 *
1458 * This function is used to create an id for a client to use with DDNS
1459 * This version of the function is for the standard style, RFC 4701
1460 *
1461 * This function takes information from the type and data fields and
1462 * mangles it into a dhcid string which it places in ddns_cb.  It also
1463 * sets a field in ddns_cb to specify the class that should be used
1464 * when sending the dhcid, in this case it is a DHCID record so we use
1465 * dns_rdatatype_dhcid
1466 *
1467 * The DHCID we construct is:
1468 *  2 bytes - identifier type (see 4701 and IANA)
1469 *  1 byte  - digest type, currently only SHA256 (1)
1470 *  n bytes - digest, length depends on digest type, currently 32 for
1471 *            SHA256
1472 *
1473 * What we base the digest on is up to the calling code for an id type of
1474 * 0 - 1 octet htype followed by hlen octets of chaddr from v4 client request
1475 * 1 - data octets from a dhcpv4 client's client identifier option
1476 * 2 - the client DUID from a v4 or v6 client's client id option
1477 * This identifier is concatenated with the fqdn and the result is digested.
1478 */
1479static int get_std_dhcid(dhcp_ddns_cb_t *ddns_cb,
1480		  int type,
1481		  const u_int8_t *identifier,
1482		  unsigned id_len)
1483{
1484	struct data_string *id = &ddns_cb->dhcid;
1485	isc_md_t *md;
1486	isc_result_t result;
1487	unsigned char buf[256];	// XXX: big enough > 32
1488	unsigned char fwd_buf[256];
1489	unsigned fwd_buflen = 0;
1490
1491	/* Types can only be 0..(2^16)-1. */
1492	if (type < 0 || type > 65535)
1493		return (0);
1494
1495	md = isc_md_new();
1496	if (md == NULL) {
1497		return (0);
1498	}
1499
1500	/* We need to convert the fwd name to wire representation */
1501	if (MRns_name_pton((char *)ddns_cb->fwd_name.data, fwd_buf, 256) == -1)
1502		return (0);
1503	while(fwd_buf[fwd_buflen] != 0) {
1504		fwd_buflen += fwd_buf[fwd_buflen] + 1;
1505	}
1506	fwd_buflen++;
1507
1508	if (!buffer_allocate(&id->buffer,
1509			     ISC_SHA256_DIGESTLENGTH + 2 + 1,
1510			     MDL))
1511		return (0);
1512	id->data = id->buffer->data;
1513
1514	/* The two first bytes contain the type identifier. */
1515	putUShort(id->buffer->data, (unsigned)type);
1516
1517	/* The next is the digest type, SHA-256 is 1 */
1518	putUChar(id->buffer->data + 2, 1u);
1519
1520
1521	/* Computing the digest */
1522	result = isc_md_init(md, ISC_MD_SHA256);
1523	if (result != ISC_R_SUCCESS) {
1524		goto end;
1525	}
1526
1527	result = isc_md_update(md, identifier, id_len);
1528	if (result != ISC_R_SUCCESS) {
1529		goto end;
1530	}
1531
1532	result = isc_md_update(md, fwd_buf, fwd_buflen);
1533	if (result != ISC_R_SUCCESS) {
1534		goto end;
1535	}
1536
1537	result = isc_md_final(md, buf, &id_len);
1538	if (result != ISC_R_SUCCESS) {
1539		goto end;
1540	}
1541
1542	isc_md_free(md);
1543	md = NULL;
1544
1545	memcpy(id->buffer->data + 3, &buf, ISC_SHA256_DIGESTLENGTH);
1546
1547	id->len = ISC_SHA256_DIGESTLENGTH + 2 + 1;
1548
1549	return (1);
1550end:
1551	if (md != NULL) {
1552		isc_md_free(md);
1553	}
1554	return (0);
1555}
1556
1557/*!
1558 *
1559 * \brief Create an id for a client
1560 *
1561 * This function is used to create an id for a client to use with DDNS
1562 * This version of the function is for the interim style.  It is retained
1563 * to allow users to continue using the interim style but they should
1564 * switch to the standard style (which uses get_std_dhcid) for better
1565 * interoperability.
1566 *
1567 * This function takes information from the type and data fields and
1568 * mangles it into a dhcid string which it places in ddns_cb.  It also
1569 * sets a field in ddns_cb to specify the class that should be used
1570 * when sending the dhcid, in this case it is a txt record so we use
1571 * dns_rdata_type_txt
1572 *
1573 * NOTE WELL: this function has issues with how it calculates the
1574 * dhcid, they can't be changed now as that would break the records
1575 * already in use.
1576 */
1577
1578static int get_int_dhcid (dhcp_ddns_cb_t *ddns_cb,
1579		   int type,
1580		   const u_int8_t *data,
1581		   unsigned len)
1582{
1583	struct data_string *id = &ddns_cb->dhcid;
1584	unsigned char buf[256];	// XXX: big enough (> 16)
1585	isc_md_t *md;
1586	isc_result_t result;
1587	int i;
1588
1589	/* Types can only be 0..(2^16)-1. */
1590	if (type < 0 || type > 65535)
1591		return (0);
1592
1593	/*
1594	 * Hexadecimal MD5 digest plus two byte type, NUL,
1595	 * and one byte for length for dns.
1596	 */
1597	if (!buffer_allocate(&id -> buffer,
1598			     (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
1599		return (0);
1600	id->data = id->buffer->data;
1601
1602	/*
1603	 * We put the length into the first byte to turn
1604	 * this into a dns text string.  This avoid needing to
1605	 * copy the string to add the byte later.
1606	 */
1607	id->buffer->data[0] = ISC_MD5_DIGESTLENGTH * 2 + 2;
1608
1609	/* Put the type in the next two bytes. */
1610	id->buffer->data[1] = "0123456789abcdef"[(type >> 4) & 0xf];
1611	/* This should have been [type & 0xf] but now that
1612	 * it is in use we need to leave it this way in order
1613	 * to avoid disturbing customer's lease files
1614	 */
1615	id->buffer->data[2] = "0123456789abcdef"[type % 15];
1616
1617	/* Mash together an MD5 hash of the identifier. */
1618	md = isc_md_new();
1619	if (md == NULL) {
1620		return (0);
1621	}
1622
1623	result = isc_md_init(md, ISC_MD_MD5);
1624	if (result != ISC_R_SUCCESS) {
1625		goto end;
1626	}
1627
1628	result = isc_md_update(md, data, len);
1629	if (result != ISC_R_SUCCESS) {
1630		goto end;
1631	}
1632
1633	result = isc_md_final(md, buf, &len);
1634	if (result != ISC_R_SUCCESS) {
1635		goto end;
1636	}
1637
1638	isc_md_free(md);
1639	md = NULL;
1640
1641	/* Convert into ASCII. */
1642	for (i = 0; i < ISC_MD5_DIGESTLENGTH; i++) {
1643		id->buffer->data[i * 2 + 3] =
1644			"0123456789abcdef"[(buf[i] >> 4) & 0xf];
1645		id->buffer->data[i * 2 + 4] =
1646			"0123456789abcdef"[buf[i] & 0xf];
1647	}
1648
1649	id->len = ISC_MD5_DIGESTLENGTH * 2 + 3;
1650	id->buffer->data[id->len] = 0;
1651	id->terminated = 1;
1652
1653	return (1);
1654end:
1655	if (md != NULL) {
1656		isc_md_free(md);
1657	}
1658	return (0);
1659}
1660
1661int get_dhcid(dhcp_ddns_cb_t *ddns_cb,
1662	      int type,
1663	      const u_int8_t *identifier,
1664	      unsigned id_len)
1665{
1666	if (ddns_cb->dhcid_class == dns_rdatatype_dhcid)
1667		return get_std_dhcid(ddns_cb, type, identifier, id_len);
1668	else
1669		return get_int_dhcid(ddns_cb, type, identifier, id_len);
1670}
1671
1672/*
1673 * The dhcid (text version) that we pass to DNS includes a length byte
1674 * at the start but the text we store in the lease doesn't include the
1675 * length byte.  The following routines are to convert between the two
1676 * styles.
1677 *
1678 * When converting from a dhcid to a leaseid we reuse the buffer and
1679 * simply adjust the data pointer and length fields in the data string.
1680 * This avoids any prolems with allocating space.
1681 */
1682
1683void
1684dhcid_tolease(struct data_string *dhcid,
1685	      struct data_string *leaseid)
1686{
1687	/* copy the data string then update the fields */
1688	data_string_copy(leaseid, dhcid, MDL);
1689	leaseid->data++;
1690	leaseid->len--;
1691}
1692
1693isc_result_t
1694dhcid_fromlease(struct data_string *dhcid,
1695		struct data_string *leaseid)
1696{
1697	if (!buffer_allocate(&dhcid->buffer, leaseid->len + 2, MDL)) {
1698		return(ISC_R_FAILURE);
1699	}
1700
1701	dhcid->data = dhcid->buffer->data;
1702
1703	dhcid->buffer->data[0] = leaseid->len;
1704	memcpy(dhcid->buffer->data + 1, leaseid->data, leaseid->len);
1705	dhcid->len = leaseid->len + 1;
1706	if (leaseid->terminated == 1) {
1707		dhcid->buffer->data[dhcid->len] = 0;
1708		dhcid->terminated = 1;
1709	}
1710
1711	return(ISC_R_SUCCESS);
1712}
1713
1714/*
1715 * Construct the dataset for this item.
1716 * This is a fairly simple arrangement as the operations we do are simple.
1717 * If there is data we simply have the rdata point to it - the formatting
1718 * must be correct already.  We then link the rdatalist to the rdata and
1719 * create a rdataset from the rdatalist.
1720 */
1721
1722static isc_result_t
1723make_dns_dataset(dns_rdataclass_t  dataclass,
1724		 dns_rdatatype_t   datatype,
1725		 dhcp_ddns_data_t *dataspace,
1726		 unsigned char    *data,
1727		 int               datalen,
1728		 int               ttl)
1729{
1730	dns_rdata_t *rdata = &dataspace->rdata;
1731	dns_rdatalist_t *rdatalist = &dataspace->rdatalist;
1732	dns_rdataset_t *rdataset = &dataspace->rdataset;
1733
1734	isc_region_t region;
1735
1736	/* set up the rdata */
1737	dns_rdata_init(rdata);
1738
1739	if (data == NULL) {
1740		/* No data, set up the rdata fields we care about */
1741		rdata->flags = DNS_RDATA_UPDATE;
1742		rdata->type = datatype;
1743		rdata->rdclass = dataclass;
1744	} else {
1745		switch(datatype) {
1746		case dns_rdatatype_a:
1747		case dns_rdatatype_aaaa:
1748		case dns_rdatatype_txt:
1749		case dns_rdatatype_dhcid:
1750		case dns_rdatatype_ptr:
1751			/* The data must be in the right format we simply
1752			 * need to supply it via the correct structure */
1753			region.base   = data;
1754			region.length = datalen;
1755			dns_rdata_fromregion(rdata, dataclass, datatype,
1756					     &region);
1757			break;
1758		default:
1759			return(DHCP_R_INVALIDARG);
1760			break;
1761		}
1762	}
1763
1764	/* setup the datalist and attach the rdata to it */
1765	dns_rdatalist_init(rdatalist);
1766	rdatalist->type = datatype;
1767	rdatalist->rdclass = dataclass;
1768	rdatalist->ttl = ttl;
1769	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1770
1771	/* convert the datalist to a dataset */
1772	dns_rdataset_init(rdataset);
1773	dns_rdatalist_tordataset(rdatalist, rdataset);
1774
1775	return(ISC_R_SUCCESS);
1776}
1777
1778#if defined (DEBUG_DNS_UPDATES)
1779static void log_call(char *text, dns_name_t* pname, dns_name_t* uname) {
1780    char buf1[512];
1781    char buf2[512];
1782    if (pname) {
1783        dns_name_format(pname, buf1, 512);
1784    } else {
1785        *buf1=0;
1786    }
1787
1788    if (uname) {
1789        dns_name_format(uname, buf2, 512);
1790    } else {
1791        *buf2=0;
1792    }
1793
1794    log_info ("DDNS: %s: pname:[%s] uname:[%s]", text, buf1, buf2);
1795}
1796#endif
1797
1798
1799/*
1800 * When a DHCP client or server intends to update an A RR, it first
1801 * prepares a DNS UPDATE query which includes as a prerequisite the
1802 * assertion that the name does not exist.  The update section of the
1803 * query attempts to add the new name and its IP address mapping (an A
1804 * RR), and the DHCID RR with its unique client-identity.
1805 *   -- "Interaction between DHCP and DNS"
1806 *
1807 * There are two cases, one for the server and one for the client.
1808 *
1809 * For the server the first step will have a request of:
1810 * The name is not in use
1811 * Add an A RR
1812 * Add a DHCID RR
1813 *
1814 * For the client the first step will have a request of:
1815 * The A RR does not exist
1816 * Add an A RR
1817 * Add a DHCID RR
1818 */
1819
1820static isc_result_t
1821build_fwd_add1(dhcp_ddns_cb_t   *ddns_cb,
1822		     dhcp_ddns_data_t *dataspace,
1823		     dns_name_t       *pname,
1824		     dns_name_t       *uname)
1825{
1826	isc_result_t result;
1827
1828#if defined (DEBUG_DNS_UPDATES)
1829	log_call("build_fwd_add1", pname, uname);
1830#endif
1831
1832	/* Construct the prerequisite list */
1833	if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) {
1834		/* The A RR shouldn't exist */
1835		result = make_dns_dataset(dns_rdataclass_none,
1836					  ddns_cb->address_type,
1837					  dataspace, NULL, 0, 0);
1838	} else {
1839		/* The name is not in use */
1840		result = make_dns_dataset(dns_rdataclass_none,
1841					  dns_rdatatype_any,
1842					  dataspace, NULL, 0, 0);
1843	}
1844	if (result != ISC_R_SUCCESS) {
1845		return(result);
1846	}
1847	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1848	dataspace++;
1849
1850	/* Construct the update list */
1851	/* Add the A RR */
1852	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1853				  dataspace,
1854				  (unsigned char *)ddns_cb->address.iabuf,
1855				  ddns_cb->address.len, ddns_cb->ttl);
1856	if (result != ISC_R_SUCCESS) {
1857		return(result);
1858	}
1859	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1860	dataspace++;
1861
1862	/* Add the DHCID RR */
1863	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
1864				  dataspace,
1865				  (unsigned char *)ddns_cb->dhcid.data,
1866				  ddns_cb->dhcid.len, ddns_cb->ttl);
1867	if (result != ISC_R_SUCCESS) {
1868		return(result);
1869	}
1870	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1871
1872	return(ISC_R_SUCCESS);
1873}
1874
1875/*
1876 * If the first update operation fails with YXDOMAIN, the updater can
1877 * conclude that the intended name is in use.  The updater then
1878 * attempts to confirm that the DNS name is not being used by some
1879 * other host. The updater prepares a second UPDATE query in which the
1880 * prerequisite is that the desired name has attached to it a DHCID RR
1881 * whose contents match the client identity.  The update section of
1882 * this query deletes the existing A records on the name, and adds the
1883 * A record that matches the DHCP binding and the DHCID RR with the
1884 * client identity.
1885 *   -- "Interaction between DHCP and DNS"
1886 *
1887 * The message for the second step depends on if we are doing conflict
1888 * resolution.  If we are we include the prerequisite.  The prerequiste
1889 * will either:
1890 *  A. require the data value of the DHCID RR to match that of the client
1891 * or
1892 *  B. required only that the DHCID RR of the configured class (DHCID or
1893 * TXT) exist
1894 *
1895 * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off.
1896 *
1897 * The prerequisite is omitted if conflict detection is off.
1898 *
1899 * If not we delete the DHCID in addition to all A rrsets.
1900 *
1901 * Conflict resolution:
1902 * DHCID RR exists, and matches client identity.
1903 * Delete A RRset.
1904 * Add A RR.
1905 *
1906 * Conflict override:
1907 * Delete DHCID RRs.
1908 * Add DHCID RR
1909 * Delete A RRset.
1910 * Add A RR.
1911 */
1912
1913static isc_result_t
1914build_fwd_add2(dhcp_ddns_cb_t   *ddns_cb,
1915		     dhcp_ddns_data_t *dataspace,
1916		     dns_name_t       *pname,
1917		     dns_name_t       *uname)
1918{
1919	isc_result_t result = ISC_R_SUCCESS;
1920
1921#if defined (DEBUG_DNS_UPDATES)
1922	log_call("build_fwd_add2", pname, uname);
1923#endif
1924
1925	/*
1926	 * If we are doing conflict detection we use a prereq list.
1927	 * If not we delete the DHCID in addition to all A rrsets.
1928	 */
1929	if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
1930		/* Construct the prereq list */
1931		/* The DHCID RR exists and optionally matches the client's
1932		 * identity.  If matching is turned off, we use the presence
1933		 * of a DHCID RR to signal that this is a dynamic entry and
1934		 * thus eligible for us to overwrite.  If matching is on
1935		 * then we can only replace the entries if they belong to
1936		 * this client. */
1937		unsigned char *match_id = NULL;
1938		int match_id_len = 0;
1939		int match_class = dns_rdataclass_any;
1940		if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
1941			match_id = (unsigned char*)(ddns_cb->dhcid.data);
1942			match_id_len = ddns_cb->dhcid.len;
1943			match_class = dns_rdataclass_in;
1944		}
1945
1946		result = make_dns_dataset(match_class,
1947					  ddns_cb->dhcid_class,
1948					  dataspace,
1949					  match_id, match_id_len, 0);
1950		if (result != ISC_R_SUCCESS) {
1951			return(result);
1952		}
1953		ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
1954		dataspace++;
1955	} else {
1956		/* Start constructing the update list.
1957		 * Conflict detection override: delete DHCID RRs */
1958		result = make_dns_dataset(dns_rdataclass_any,
1959					  ddns_cb->dhcid_class,
1960					  dataspace, NULL, 0, 0);
1961		if (result != ISC_R_SUCCESS) {
1962			return(result);
1963		}
1964		ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1965		dataspace++;
1966
1967		/* Add current DHCID RR, always include client id */
1968		result = make_dns_dataset(dns_rdataclass_in,
1969					  ddns_cb->dhcid_class,
1970					  dataspace,
1971					  (unsigned char *)ddns_cb->dhcid.data,
1972					  ddns_cb->dhcid.len, ddns_cb->ttl);
1973		if (result != ISC_R_SUCCESS) {
1974			return(result);
1975		}
1976		ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1977		dataspace++;
1978	}
1979
1980	/* Start or continue constructing the update list */
1981	/* Delete the address RRset */
1982	result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type,
1983				  dataspace, NULL, 0, 0);
1984	if (result != ISC_R_SUCCESS) {
1985		return(result);
1986	}
1987	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1988	dataspace++;
1989
1990	/* Add the address RR */
1991	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
1992				  dataspace,
1993				  (unsigned char *)ddns_cb->address.iabuf,
1994				  ddns_cb->address.len, ddns_cb->ttl);
1995	if (result != ISC_R_SUCCESS) {
1996		return(result);
1997	}
1998	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
1999
2000	return(ISC_R_SUCCESS);
2001}
2002
2003/*
2004 * Creates the DNS foward update add used for DSMM add attempt #3 and
2005 * ddns-other-guard-is-dynamic is off
2006 *
2007 *
2008 * If the second update failed with NXRRSET, this indicates that:
2009 *
2010 * 1. our FQDN is in use
2011 * 2  no guard record (DHCID RR) for that FQDN, of our class (and optionally
2012 * client id) exists
2013 *
2014 * In Dual Stack Mixed Mode, we need to attempt a third add, to distinguish
2015 * between static entries that we cannot modify and dynamic entries belonging
2016 * to the "other" side of dual stack.  The prerequisites for this add are:
2017 *
2018 * 1. No address record of my type exists
2019 * 2. No guard record of my type exists
2020 * 3. A guard record of the other type exists
2021 *
2022 * and updates which will add the new address and guard record:
2023 *
2024 * prereq nxrrset <name> <addr_t>           # no address record of my type
2025 * prereq nxrrset <name> <guard_t>          # no guard record of my type
2026 * prereq yxrrset <name> <other_guard_t>    # other guard type does exist
2027 * update add <name> <addr_t> <address>     # add the new address record
2028 * update add <name> <guard_t> <client-id>  # add the new address record
2029 */
2030static isc_result_t
2031build_dsmm_fwd_add3(dhcp_ddns_cb_t   *ddns_cb,
2032		     dhcp_ddns_data_t *dataspace,
2033		     dns_name_t       *pname,
2034		     dns_name_t       *uname)
2035{
2036	isc_result_t result = ISC_R_SUCCESS;
2037
2038#if defined (DEBUG_DNS_UPDATES)
2039	log_call("build_fwd_add3", pname, uname);
2040#endif
2041	/* Construct the prereq list */
2042	/* No address record of my type exists */
2043	result = make_dns_dataset(dns_rdataclass_none,
2044				  ddns_cb->address_type,
2045				  dataspace, NULL, 0, 0);
2046	if (result != ISC_R_SUCCESS) {
2047		return(result);
2048	}
2049	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2050	dataspace++;
2051
2052	/* No guard record of my type exists */
2053	result = make_dns_dataset(dns_rdataclass_none,
2054				  ddns_cb->dhcid_class,
2055				  dataspace, NULL, 0, 0);
2056	if (result != ISC_R_SUCCESS) {
2057		return(result);
2058	}
2059	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2060	dataspace++;
2061
2062	/* Guard record of the other type DOES exist */
2063	result = make_dns_dataset(dns_rdataclass_any,
2064				  ddns_cb->other_dhcid_class,
2065				  dataspace, NULL, 0, 0);
2066	if (result != ISC_R_SUCCESS) {
2067		return(result);
2068	}
2069	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2070	dataspace++;
2071
2072	/* Start constructing the update list. */
2073	/* Add the address RR */
2074	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
2075				  dataspace,
2076				  (unsigned char *)ddns_cb->address.iabuf,
2077				  ddns_cb->address.len, ddns_cb->ttl);
2078	if (result != ISC_R_SUCCESS) {
2079		return(result);
2080	}
2081	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2082	dataspace++;
2083
2084	/* Add current DHCID RR */
2085	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
2086				  dataspace,
2087				  (unsigned char *)ddns_cb->dhcid.data,
2088				  ddns_cb->dhcid.len, ddns_cb->ttl);
2089	if (result != ISC_R_SUCCESS) {
2090		return(result);
2091	}
2092	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2093
2094	return(ISC_R_SUCCESS);
2095}
2096
2097/*
2098 * Creates the DNS foward update add used for DSMM add attempt #3 and
2099 * ddns-other-guard-is-dynamic is ON
2100 *
2101 * If the second update failed with NXRRSET, this indicates that:
2102 *
2103 * 1. our FQDN is in use
2104 * 2  no guard record (DHCID RR) for that FQDN, of our class (and optionally
2105 * client id) exists
2106 *
2107 * When we're In Dual Stack Mixed Mode and ddns-other-guard-is-dynamic is ON
2108 * we need only determine if a guard record of the other type exists, to know
2109 * if we can add/replace and address record of our type.   In other words,
2110 * the presence of a dynamic entry belonging to the "other" stack means
2111 * all entries for this name should be dynamic and we overwrite an unguarded
2112 * address record of our type.
2113 *
2114 * The udpate will contain a single prequisite for a guard record of the
2115 * other type, an update to delete any address records of our type, and
2116 * updates to add the address and guard records:
2117 *
2118 * prereq yxrrset <name> <other_guard_t>   # other guard type exists
2119 * update delete <name> <addr_t>           # delete existing address record
2120 *                                         # (if one)
2121 * update add <name> <addr_t> <address>    # add new address record
2122 * update add <name> <guard_t> <client-id> # add new guard record
2123 */
2124static isc_result_t
2125build_dsmm_fwd_add3_other(dhcp_ddns_cb_t   *ddns_cb,
2126		     dhcp_ddns_data_t *dataspace,
2127		     dns_name_t       *pname,
2128		     dns_name_t       *uname)
2129{
2130	isc_result_t result = ISC_R_SUCCESS;
2131
2132#if defined (DEBUG_DNS_UPDATES)
2133	log_call("build_fwd_add3_other", pname, uname);
2134#endif
2135	/* Construct the prereq list */
2136
2137	// If ID matching is on, a result of NXRRSET from add2 means
2138	// either there is no guard of my type, or there is but
2139	// it does not match this client.  We need to distinguish
2140	// between those two cases here and only allow this add
2141	// if there is no guard of my type.
2142	if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2143		/* No guard record of my type exists */
2144		result = make_dns_dataset(dns_rdataclass_none,
2145					  ddns_cb->dhcid_class,
2146					  dataspace, NULL, 0, 0);
2147		if (result != ISC_R_SUCCESS) {
2148			return(result);
2149		}
2150
2151		ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2152		dataspace++;
2153	}
2154
2155	/* A guard record of the other type exists */
2156	result = make_dns_dataset(dns_rdataclass_any,
2157				  ddns_cb->other_dhcid_class,
2158				  dataspace, NULL, 0, 0);
2159	if (result != ISC_R_SUCCESS) {
2160		return(result);
2161	}
2162	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2163	dataspace++;
2164
2165	/* Start constructing the update list. */
2166	/* Delete the existing address record of my type (if one) */
2167	result = make_dns_dataset(dns_rdataclass_any,
2168				  ddns_cb->address_type,
2169				  dataspace, NULL, 0, 0);
2170	if (result != ISC_R_SUCCESS) {
2171		return(result);
2172	}
2173	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2174	dataspace++;
2175
2176	/* Add the address RR */
2177	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
2178				  dataspace,
2179				  (unsigned char *)ddns_cb->address.iabuf,
2180				  ddns_cb->address.len, ddns_cb->ttl);
2181	if (result != ISC_R_SUCCESS) {
2182		return(result);
2183	}
2184	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2185	dataspace++;
2186
2187	/* Add current DHCID RR */
2188	result = make_dns_dataset(dns_rdataclass_in, ddns_cb->dhcid_class,
2189				  dataspace,
2190				  (unsigned char *)ddns_cb->dhcid.data,
2191				  ddns_cb->dhcid.len, ddns_cb->ttl);
2192	if (result != ISC_R_SUCCESS) {
2193		return(result);
2194	}
2195	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2196
2197	return(ISC_R_SUCCESS);
2198}
2199
2200/*
2201 * The entity chosen to handle the A record for this client (either the
2202 * client or the server) SHOULD delete the A (or AAAA) record that was
2203 * added when the lease was made to the client.
2204 *
2205 * If we are doing conflict resolution, the udpate will contain a prequisite
2206 * that will either:
2207 *  A. require that a guard record of the configure class (DHCID or TXT) with
2208 *  a data value matching that the client exist (per RFC 4703)
2209 * or
2210 *  B. require only that the guard record of the configured class exist
2211 *
2212 * based on whether DDNS_GUARD_ID_MUST_MATCH is on (default) or off.
2213 *
2214 * The prerequisite is omitted if conflict detection is off.
2215 *
2216 */
2217static isc_result_t
2218build_fwd_rem1(dhcp_ddns_cb_t   *ddns_cb,
2219		     dhcp_ddns_data_t *dataspace,
2220		     dns_name_t       *pname,
2221		     dns_name_t       *uname)
2222{
2223	isc_result_t result = ISC_R_SUCCESS;
2224
2225#if defined (DEBUG_DNS_UPDATES)
2226	log_call("build_fwd_rem1", pname, uname);
2227#endif
2228
2229	/* If we're doing conflict detection, add the guard record pre-req */
2230	if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
2231		/* Construct the prereq list */
2232		/* The guard record exists and optionally matches the client's
2233		 * identity.  If matching is turned off, we use the presence
2234		 * of a DHCID RR to signal that this is a dynamic entry and
2235		 * thus eligible for us to overwrite.  If matching is on
2236		 * then we can only delete the entries if they belong to
2237		 * this client. */
2238		unsigned char *match_id = NULL;
2239		int match_id_len = 0;
2240		int match_class = dns_rdataclass_any;
2241		if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2242			match_id = (unsigned char*)(ddns_cb->dhcid.data);
2243			match_id_len = ddns_cb->dhcid.len;
2244			match_class = dns_rdataclass_in;
2245		}
2246
2247		result = make_dns_dataset(match_class,
2248					  ddns_cb->dhcid_class,
2249					  dataspace,
2250					  match_id, match_id_len, 0);
2251		if (result != ISC_R_SUCCESS) {
2252			return(result);
2253		}
2254		ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2255		dataspace++;
2256	}
2257
2258	/* Construct the update list */
2259	/* Delete A RRset */
2260	result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
2261				  dataspace,
2262				  (unsigned char *)ddns_cb->address.iabuf,
2263				  ddns_cb->address.len, 0);
2264	if (result != ISC_R_SUCCESS) {
2265		return(result);
2266	}
2267	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2268
2269	return(ISC_R_SUCCESS);
2270}
2271
2272/*
2273 * If the deletion of the A succeeded, and there are no A or AAAA
2274 * records left for this domain, then we can blow away the DHCID
2275 * record as well.   We can't blow away the DHCID record above
2276 * because it's possible that more than one record has been added
2277 * to this domain name.
2278 *
2279 * Second query has:
2280 * A RR does not exist.
2281 * AAAA RR does not exist.
2282 * Delete appropriate DHCID RR.
2283 */
2284static isc_result_t
2285build_fwd_rem2(dhcp_ddns_cb_t   *ddns_cb,
2286		     dhcp_ddns_data_t *dataspace,
2287		     dns_name_t       *pname,
2288		     dns_name_t       *uname)
2289{
2290	isc_result_t result;
2291	unsigned char *match_id = NULL;
2292	int match_id_len = 0;
2293	int match_class = dns_rdataclass_any;
2294
2295#if defined (DEBUG_DNS_UPDATES)
2296	log_call("build_fwd_rem2", pname, uname);
2297#endif
2298
2299	/* Construct the prereq list */
2300	/* The A RR does not exist */
2301	result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_a,
2302				  dataspace, NULL, 0, 0);
2303	if (result != ISC_R_SUCCESS) {
2304		return(result);
2305	}
2306	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2307	dataspace++;
2308
2309	/* The AAAA RR does not exist */
2310	result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_aaaa,
2311				  dataspace, NULL, 0, 0);
2312	if (result != ISC_R_SUCCESS) {
2313		return(result);
2314	}
2315	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2316	dataspace++;
2317
2318	/* Construct the update list */
2319	/* Delete DHCID RR */
2320
2321	/* We'll specify the client id in the guard record delete if
2322	 * matching is enabled, otherwise we leave it off. */
2323	if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2324		match_id = (unsigned char*)(ddns_cb->dhcid.data);
2325		match_id_len = ddns_cb->dhcid.len;
2326		match_class = dns_rdataclass_none;
2327	}
2328
2329	result = make_dns_dataset(match_class, ddns_cb->dhcid_class,
2330				  dataspace,
2331				  match_id, match_id_len, 0);
2332	if (result != ISC_R_SUCCESS) {
2333		return(result);
2334	}
2335	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2336
2337	return(ISC_R_SUCCESS);
2338}
2339
2340/*
2341 * Constructs the second stage forward remove, when the first stage
2342 * succeeds and DSMM is enabled, and ddns-other-guard-is-dynamic is OFF
2343 *
2344 * Normal conflict detection requires that the guard record of the
2345 * configured type only be deleted if there are no address records of
2346 * any type.  In Dual Stack Mixed Mode, we are only concerned with whether
2347 * there any records or our configured address type remaining.
2348 *
2349 * This update consists of a single prequisite that there be no address
2350 * records of our type followed by a delete of the guard record of our type
2351 * and optionally matching client-id.
2352 *
2353 * prereq nxrrset name <addr_t>     # no records of this address type exist
2354 * update delete name <guard_t> <client_id>  # delete the existing guard record
2355 */
2356static isc_result_t
2357build_fwd_rem2_dsmm (dhcp_ddns_cb_t   *ddns_cb,
2358		     dhcp_ddns_data_t *dataspace,
2359		     dns_name_t       *pname,
2360		     dns_name_t       *uname)
2361{
2362	isc_result_t result;
2363	unsigned char *match_id = NULL;
2364	int match_id_len = 0;
2365	int match_class = dns_rdataclass_any;
2366
2367#if defined (DEBUG_DNS_UPDATES)
2368	log_call("build_fwd_rem2_dsmm", pname, uname);
2369#endif
2370
2371	/* Construct the prereq list */
2372	/* The address RR does not exist */
2373	result = make_dns_dataset(dns_rdataclass_none,
2374				  ddns_cb->address_type,
2375				  dataspace, NULL, 0, 0);
2376	if (result != ISC_R_SUCCESS) {
2377		return(result);
2378	}
2379	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2380	dataspace++;
2381
2382	/* Construct the update list */
2383	/* Delete DHCID RR */
2384
2385	/* We'll specify the client id in the guard record delete if
2386	 * matching is enabled, otherwise we leave it off. */
2387	if (ddns_cb->flags & DDNS_GUARD_ID_MUST_MATCH) {
2388		match_id = (unsigned char*)(ddns_cb->dhcid.data);
2389		match_id_len = ddns_cb->dhcid.len;
2390		match_class = dns_rdataclass_none;
2391	}
2392
2393	result = make_dns_dataset(match_class, ddns_cb->dhcid_class,
2394				  dataspace,
2395				  match_id, match_id_len, 0);
2396	if (result != ISC_R_SUCCESS) {
2397		return(result);
2398	}
2399	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2400
2401	return(ISC_R_SUCCESS);
2402}
2403
2404/*
2405 * Constructs the second stage forward remove, when the first stage
2406 * succeeds and DSMM is enabled and ddns-other-guard-is-dynamic is ON
2407 *
2408 * This update addresses the case when an address record of our type exists
2409 * without a guard record of our type, yet a dynamic entry of the other type
2410 * exists.  The presence of a guard of the other type indicates that all
2411 * entries for this name should be treated as dynamic, thus permitting us to
2412 * remove the address record of our type.
2413 *
2414 * prereq nxrrset <name> <guard_t>        # my guard type does not exist
2415 * prereq yxrrset <name> <other_guard_t>  # other guard type does exist
2416 * update delete <name> <addr_t> address  # delete the existing address record
2417 *
2418 */
2419static isc_result_t
2420build_fwd_rem2_dsmm_other(dhcp_ddns_cb_t   *ddns_cb,
2421		     dhcp_ddns_data_t *dataspace,
2422		     dns_name_t       *pname,
2423		     dns_name_t       *uname)
2424{
2425	isc_result_t result;
2426
2427#if defined (DEBUG_DNS_UPDATES)
2428	log_call("build_fwd_rem2_dsmm_other", pname, uname);
2429#endif
2430
2431	/* Construct the prereq list */
2432	/* No guard record of my type exists */
2433	result = make_dns_dataset(dns_rdataclass_none, ddns_cb->dhcid_class,
2434				  dataspace, NULL, 0, 0);
2435	if (result != ISC_R_SUCCESS) {
2436		return(result);
2437	}
2438	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2439	dataspace++;
2440
2441	/* Guard record of the OTHER type DOES exist */
2442	result = make_dns_dataset(dns_rdataclass_any,
2443				  ddns_cb->other_dhcid_class,
2444				  dataspace, NULL, 0, 0);
2445	if (result != ISC_R_SUCCESS) {
2446		return(result);
2447	}
2448	ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
2449	dataspace++;
2450
2451	/* Construct the update list */
2452	/* Delete the address RRset */
2453	result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
2454				  dataspace,
2455				  (unsigned char *)ddns_cb->address.iabuf,
2456				  ddns_cb->address.len, 0);
2457	if (result != ISC_R_SUCCESS) {
2458		return(result);
2459	}
2460	ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
2461
2462	return(ISC_R_SUCCESS);
2463}
2464
2465/*
2466 * This routine converts from the task action call into something
2467 * easier to work with.  It also handles the common case of a signature
2468 * or zone not being correct.
2469 */
2470void ddns_interlude(isc_task_t  *taskp,
2471		    isc_event_t *eventp)
2472{
2473	dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)eventp->ev_arg;
2474	dns_clientupdateevent_t *ddns_event = (dns_clientupdateevent_t *)eventp;
2475	isc_result_t eresult = ddns_event->result;
2476	isc_result_t result;
2477
2478	/* We've extracted the information we want from it, get rid of
2479	 * the event block.*/
2480	isc_event_free(&eventp);
2481
2482#if defined (TRACING)
2483	if (trace_record()) {
2484		trace_ddns_input_write(ddns_cb, eresult);
2485	}
2486#endif
2487
2488#if defined (DEBUG_DNS_UPDATES)
2489	print_dns_status(DDNS_PRINT_INBOUND, ddns_cb, eresult);
2490#endif
2491
2492	/* This transaction is complete, clear the value */
2493	dns_client_destroyupdatetrans(&ddns_cb->transaction);
2494
2495	/* If we cancelled or tried to cancel the operation we just
2496	 * need to clean up. */
2497	if ((eresult == ISC_R_CANCELED) ||
2498	    ((ddns_cb->flags & DDNS_ABORT) != 0)) {
2499#if defined (DEBUG_DNS_UPDATES)
2500		log_info("DDNS: completeing transaction cancellation cb=%p, "
2501			 "flags=%x, %s",
2502			 ddns_cb, ddns_cb->flags, isc_result_totext(eresult));
2503#endif
2504		if ((ddns_cb->flags & DDNS_ABORT) == 0) {
2505			log_info("DDNS: cleaning up lease pointer for a cancel "
2506				 "cb=%p", ddns_cb);
2507			/*
2508			 * We shouldn't actually be able to get here but
2509			 * we are.  This means we haven't cleaned up
2510			 * the lease pointer so we need to do that before
2511			 * freeing the cb.
2512			 */
2513			ddns_cb->cur_func(ddns_cb, eresult);
2514			return;
2515		}
2516
2517		if (ddns_cb->next_op != NULL) {
2518			/* if necessary cleanup up next op block */
2519			ddns_cb_free(ddns_cb->next_op, MDL);
2520		}
2521		ddns_cb_free(ddns_cb, MDL);
2522		return;
2523	}
2524
2525	/* If we had a problem with our key or zone try again */
2526	if ((eresult == DNS_R_NOTAUTH) ||
2527	    (eresult == DNS_R_NOTZONE)) {
2528		int i;
2529		/* Our zone information was questionable,
2530		 * repudiate it and try again */
2531		log_error("DDNS: bad zone information, repudiating zone %s",
2532			  ddns_cb->zone_name);
2533		repudiate_zone(&ddns_cb->zone);
2534		ddns_cb->zone_name[0]    = 0;
2535		ISC_LIST_INIT(ddns_cb->zone_server_list);
2536		for (i = 0; i < DHCP_MAXNS; i++) {
2537			ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
2538		}
2539
2540		if ((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
2541		    (ddns_cb->state == DDNS_STATE_REM_PTR)) {
2542			result = ddns_modify_ptr(ddns_cb, MDL);
2543		} else {
2544			result = ddns_modify_fwd(ddns_cb, MDL);
2545		}
2546
2547		if (result != ISC_R_SUCCESS) {
2548			/* if we couldn't redo the query log it and
2549			 * let the next function clean it up */
2550			log_info("DDNS: Failed to retry after zone failure");
2551			ddns_cb->cur_func(ddns_cb, result);
2552		}
2553		return;
2554	} else {
2555		/* pass it along to be processed */
2556		ddns_cb->cur_func(ddns_cb, eresult);
2557	}
2558
2559	return;
2560}
2561
2562/*
2563 * This routine does the generic work for sending a ddns message to
2564 * modify the forward record (A or AAAA) and calls one of a set of
2565 * routines to build the specific message.
2566 */
2567
2568isc_result_t
2569ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
2570{
2571	isc_result_t result;
2572	dns_tsec_t *tsec_key = NULL;
2573
2574#if defined (DEBUG_DNS_UPDATES)
2575	log_info("DDNS: ddns_modify_fwd");
2576#endif
2577
2578	unsigned char *clientname;
2579	dhcp_ddns_data_t *dataspace = NULL;
2580	dns_namelist_t prereqlist, updatelist;
2581	dns_fixedname_t zname0, pname0, uname0;
2582	dns_name_t *zname = NULL, *pname, *uname;
2583
2584	isc_sockaddrlist_t *zlist = NULL;
2585
2586	/* Creates client context if we need to */
2587	result = dns_client_init();
2588	if (result != ISC_R_SUCCESS) {
2589		return result;
2590	}
2591
2592	/* Get a pointer to the clientname to make things easier. */
2593	clientname = (unsigned char *)ddns_cb->fwd_name.data;
2594
2595	/* Extract and validate the type of the address. */
2596	if (ddns_cb->address.len == 4) {
2597		ddns_cb->address_type = dns_rdatatype_a;
2598	} else if (ddns_cb->address.len == 16) {
2599		ddns_cb->address_type = dns_rdatatype_aaaa;
2600	} else {
2601		return DHCP_R_INVALIDARG;
2602	}
2603
2604	/*
2605	 * If we already have a zone use it, otherwise try to lookup the
2606	 * zone in our cache.  If we find one we will have a pointer to
2607	 * the zone that needs to be dereferenced when we are done with it.
2608	 * If we don't find one that is okay we'll let the DNS code try and
2609	 * find the information for us.
2610	 */
2611
2612	if (ddns_cb->zone == NULL) {
2613		result = find_cached_zone(ddns_cb, FIND_FORWARD);
2614#if defined (DNS_ZONE_LOOKUP)
2615		if (result == ISC_R_NOTFOUND) {
2616			/*
2617			 * We didn't find a cached zone, see if we can
2618			 * can find a nameserver and create a zone.
2619			 */
2620			if (find_zone_start(ddns_cb, FIND_FORWARD)
2621			    == ISC_R_SUCCESS) {
2622				/*
2623				 * We have started the process to find a zone
2624				 * queue the ddns_cb for processing after we
2625				 * create the zone
2626				 */
2627				/* sar - not yet implemented, currently we just
2628				 * arrange for things to get cleaned up
2629				 */
2630				goto cleanup;
2631			}
2632		}
2633#endif
2634		if (result != ISC_R_SUCCESS)
2635			goto cleanup;
2636	}
2637
2638	/*
2639	 * If we have a zone try to get any information we need
2640	 * from it - name, addresses and the key.  The address
2641	 * and key may be empty the name can't be.
2642	 */
2643	if (ddns_cb->zone) {
2644		/* Set up the zone name for use by DNS */
2645		result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
2646		if (result != ISC_R_SUCCESS) {
2647			log_error("Unable to build name for zone for "
2648				  "fwd update: %s %s",
2649				  ddns_cb->zone_name,
2650				  isc_result_totext(result));
2651			goto cleanup;
2652		}
2653
2654		if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
2655			/* If we have any addresses get them */
2656			zlist = &ddns_cb->zone_server_list;
2657		}
2658
2659
2660		if (ddns_cb->zone->key != NULL) {
2661			/*
2662			 * Not having a key is fine, having a key
2663			 * but not a tsec is odd so we warn the user.
2664			 */
2665			/*sar*/
2666			/* should we do the warning? */
2667			tsec_key = ddns_cb->zone->key->tsec_key;
2668			if (tsec_key == NULL) {
2669				log_error("No tsec for use with key %s",
2670					  ddns_cb->zone->key->name);
2671			}
2672		}
2673	}
2674
2675	/* Set up the DNS names for the prereq and update lists */
2676	if (((result = dhcp_isc_name(clientname, &pname0, &pname))
2677	     != ISC_R_SUCCESS) ||
2678	    ((result = dhcp_isc_name(clientname, &uname0, &uname))
2679	     != ISC_R_SUCCESS)) {
2680		log_error("Unable to build name for fwd update: %s %s",
2681			  clientname, isc_result_totext(result));
2682		goto cleanup;
2683	}
2684
2685	/* Allocate the various isc dns library structures we may require. */
2686	dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4);
2687	if (dataspace == NULL) {
2688		log_error("Unable to allocate memory for fwd update");
2689		result = ISC_R_NOMEMORY;
2690		goto cleanup;
2691	}
2692
2693	ISC_LIST_INIT(prereqlist);
2694	ISC_LIST_INIT(updatelist);
2695
2696	switch(ddns_cb->state) {
2697	case DDNS_STATE_ADD_FW_NXDOMAIN:
2698		result = build_fwd_add1(ddns_cb, dataspace, pname, uname);
2699		if (result != ISC_R_SUCCESS) {
2700			goto cleanup;
2701		}
2702		ISC_LIST_APPEND(prereqlist, pname, link);
2703		break;
2704
2705	case DDNS_STATE_ADD_FW_YXDHCID:
2706		result = build_fwd_add2(ddns_cb, dataspace, pname, uname);
2707		if (result != ISC_R_SUCCESS) {
2708			goto cleanup;
2709		}
2710
2711		/* If we are doing conflict detection we have entries
2712		 * in the pname list and we need to attach it to the
2713		 * prereqlist */
2714
2715		if (ddns_cb->flags & DDNS_CONFLICT_DETECTION) {
2716			ISC_LIST_APPEND(prereqlist, pname, link);
2717		}
2718
2719		break;
2720
2721	case DDNS_STATE_DSMM_FW_ADD3: {
2722		/* We should only be here if we're doing DSMM */
2723		builder_func_t builder;
2724		if (ddns_cb->flags & DDNS_OTHER_GUARD_IS_DYNAMIC) {
2725			builder = build_dsmm_fwd_add3_other;
2726		} else {
2727			builder = build_dsmm_fwd_add3;
2728		}
2729
2730		result = (*builder)(ddns_cb, dataspace, pname, uname);
2731		if (result != ISC_R_SUCCESS) {
2732			goto cleanup;
2733		}
2734
2735		ISC_LIST_APPEND(prereqlist, pname, link);
2736		break;
2737		}
2738
2739	case DDNS_STATE_REM_FW_YXDHCID:
2740		result = build_fwd_rem1(ddns_cb, dataspace, pname, uname);
2741		if (result != ISC_R_SUCCESS) {
2742			goto cleanup;
2743		}
2744		ISC_LIST_APPEND(prereqlist, pname, link);
2745		break;
2746
2747	case DDNS_STATE_REM_FW_NXRR: {
2748		builder_func_t builder;
2749
2750		if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
2751			builder = build_fwd_rem2_dsmm;
2752		} else {
2753			builder = build_fwd_rem2;
2754		}
2755
2756		result = (*builder)(ddns_cb, dataspace, pname, uname);
2757		if (result != ISC_R_SUCCESS) {
2758			goto cleanup; }
2759		ISC_LIST_APPEND(prereqlist, pname, link);
2760		break;
2761		}
2762
2763	case DDNS_STATE_REM_FW_DSMM_OTHER: {
2764		result = build_fwd_rem2_dsmm_other(ddns_cb, dataspace,
2765						  pname, uname);
2766		if (result != ISC_R_SUCCESS) {
2767			goto cleanup; }
2768		ISC_LIST_APPEND(prereqlist, pname, link);
2769		break;
2770		}
2771
2772	default:
2773		log_error("ddns_modify_fwd: Invalid state: %d", ddns_cb->state);
2774		result = DHCP_R_INVALIDARG;
2775		goto cleanup;
2776		break;
2777	}
2778
2779	/*
2780	 * We always have an update list but may not have a prereqlist
2781	 * if we are doing conflict override.
2782	 */
2783	ISC_LIST_APPEND(updatelist, uname, link);
2784
2785	/* send the message, cleanup and return the result */
2786	result = ddns_update(dhcp_gbl_ctx.dnsclient,
2787			     dns_rdataclass_in, zname,
2788			     &prereqlist, &updatelist,
2789			     zlist, tsec_key,
2790			     DNS_CLIENTUPDOPT_ALLOWRUN,
2791			     dhcp_gbl_ctx.task,
2792			     ddns_interlude,
2793			     (void *)ddns_cb,
2794			     &ddns_cb->transaction);
2795	if (result == ISC_R_FAMILYNOSUPPORT) {
2796		log_info("Unable to perform DDNS update, "
2797			 "address family not supported");
2798	}
2799
2800#if defined (DEBUG_DNS_UPDATES)
2801	print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
2802#endif
2803
2804 cleanup:
2805#if defined (DEBUG_DNS_UPDATES)
2806	if (result != ISC_R_SUCCESS) {
2807		log_info("DDNS: %s(%d): error in ddns_modify_fwd %s for %p",
2808			 file, line, isc_result_totext(result), ddns_cb);
2809	}
2810#endif
2811
2812	if (dataspace != NULL) {
2813		isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
2814			    sizeof(*dataspace) * 4);
2815	}
2816	return(result);
2817}
2818
2819
2820isc_result_t
2821ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
2822{
2823	isc_result_t result;
2824	dns_tsec_t *tsec_key  = NULL;
2825	unsigned char *ptrname;
2826	dhcp_ddns_data_t *dataspace = NULL;
2827	dns_namelist_t updatelist;
2828	dns_fixedname_t zname0, uname0;
2829	dns_name_t *zname = NULL, *uname;
2830	isc_sockaddrlist_t *zlist = NULL;
2831	unsigned char buf[256];
2832	int buflen;
2833
2834#if defined (DEBUG_DNS_UPDATES)
2835	log_info("DDNS: ddns_modify_ptr");
2836#endif
2837
2838	/* Creates client context if we need to */
2839	result = dns_client_init();
2840	if (result != ISC_R_SUCCESS) {
2841		return result;
2842	}
2843
2844	/*
2845	 * Try to lookup the zone in the zone cache.  As with the forward
2846	 * case it's okay if we don't have one, the DNS code will try to
2847	 * find something also if we succeed we will need to dereference
2848	 * the zone later.  Unlike with the forward case we assume we won't
2849	 * have a pre-existing zone.
2850	 */
2851	result = find_cached_zone(ddns_cb, FIND_REVERSE);
2852
2853#if defined (DNS_ZONE_LOOKUP)
2854	if (result == ISC_R_NOTFOUND) {
2855		/*
2856		 * We didn't find a cached zone, see if we can
2857		 * can find a nameserver and create a zone.
2858		 */
2859		if (find_zone_start(ddns_cb, FIND_REVERSE) == ISC_R_SUCCESS) {
2860			/*
2861			 * We have started the process to find a zone
2862			 * queue the ddns_cb for processing after we
2863			 * create the zone
2864			 */
2865			/* sar - not yet implemented, currently we just
2866			 * arrange for things to get cleaned up
2867			 */
2868			goto cleanup;
2869		}
2870	}
2871#endif
2872	if (result != ISC_R_SUCCESS)
2873		goto cleanup;
2874
2875
2876	if ((result == ISC_R_SUCCESS) &&
2877	    !(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
2878		/* Set up the zone name for use by DNS */
2879		result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
2880		if (result != ISC_R_SUCCESS) {
2881			log_error("Unable to build name for zone for "
2882				  "fwd update: %s %s",
2883				  ddns_cb->zone_name,
2884				  isc_result_totext(result));
2885			goto cleanup;
2886		}
2887		/* If we have any addresses get them */
2888		if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
2889			zlist = &ddns_cb->zone_server_list;
2890		}
2891
2892		/*
2893		 * If we now have a zone try to get the key, NULL is okay,
2894		 * having a key but not a tsec is odd so we warn.
2895		 */
2896		/*sar*/
2897		/* should we do the warning if we have a key but no tsec? */
2898		if ((ddns_cb->zone != NULL) && (ddns_cb->zone->key != NULL)) {
2899			tsec_key = ddns_cb->zone->key->tsec_key;
2900			if (tsec_key == NULL) {
2901				log_error("No tsec for use with key %s",
2902					  ddns_cb->zone->key->name);
2903			}
2904		}
2905	}
2906
2907	/* We must have a name for the update list */
2908	/* Get a pointer to the ptrname to make things easier. */
2909	ptrname = (unsigned char *)ddns_cb->rev_name.data;
2910
2911	if ((result = dhcp_isc_name(ptrname, &uname0, &uname))
2912	     != ISC_R_SUCCESS) {
2913		log_error("Unable to build name for fwd update: %s %s",
2914			  ptrname, isc_result_totext(result));
2915		goto cleanup;
2916	}
2917
2918	/*
2919	 * Allocate the various isc dns library structures we may require.
2920	 * Allocating one blob avoids being halfway through the process
2921	 * and being unable to allocate as well as making the free easy.
2922	 */
2923	dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2);
2924	if (dataspace == NULL) {
2925		log_error("Unable to allocate memory for fwd update");
2926		result = ISC_R_NOMEMORY;
2927		goto cleanup;
2928	}
2929
2930	ISC_LIST_INIT(updatelist);
2931
2932	/*
2933	 * Construct the update list
2934	 * We always delete what's currently there
2935	 * Delete PTR RR.
2936	 */
2937	result = make_dns_dataset(dns_rdataclass_any, dns_rdatatype_ptr,
2938				  &dataspace[0], NULL, 0, 0);
2939	if (result != ISC_R_SUCCESS) {
2940		goto cleanup;
2941	}
2942	ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link);
2943
2944	/*
2945	 * If we are updating the pointer we then add the new one
2946	 * Add PTR RR.
2947	 */
2948	if (ddns_cb->state == DDNS_STATE_ADD_PTR) {
2949		/*
2950		 * Need to convert pointer into on the wire representation
2951		 */
2952		if (MRns_name_pton((char *)ddns_cb->fwd_name.data,
2953				   buf, 256) == -1) {
2954			goto cleanup;
2955		}
2956		buflen = 0;
2957		while (buf[buflen] != 0) {
2958			buflen += buf[buflen] + 1;
2959		}
2960		buflen++;
2961
2962		result = make_dns_dataset(dns_rdataclass_in,
2963					  dns_rdatatype_ptr,
2964					  &dataspace[1],
2965					  buf, buflen, ddns_cb->ttl);
2966		if (result != ISC_R_SUCCESS) {
2967			goto cleanup;
2968		}
2969		ISC_LIST_APPEND(uname->list, &dataspace[1].rdataset, link);
2970	}
2971
2972	ISC_LIST_APPEND(updatelist, uname, link);
2973
2974	/*sar*/
2975	/*
2976	 * for now I'll cleanup the dataset immediately, it would be
2977	 * more efficient to keep it around in case the signaturure failed
2978	 * and we wanted to retry it.
2979	 */
2980	/* send the message, cleanup and return the result */
2981	result = ddns_update((dns_client_t *)dhcp_gbl_ctx.dnsclient,
2982			     dns_rdataclass_in, zname,
2983			     NULL, &updatelist,
2984			     zlist, tsec_key,
2985			     DNS_CLIENTUPDOPT_ALLOWRUN,
2986			     dhcp_gbl_ctx.task,
2987			     ddns_interlude, (void *)ddns_cb,
2988			     &ddns_cb->transaction);
2989	if (result == ISC_R_FAMILYNOSUPPORT) {
2990		log_info("Unable to perform DDNS update, "
2991			 "address family not supported");
2992	}
2993
2994#if defined (DEBUG_DNS_UPDATES)
2995	print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
2996#endif
2997
2998 cleanup:
2999#if defined (DEBUG_DNS_UPDATES)
3000	if (result != ISC_R_SUCCESS) {
3001		log_info("DDNS: %s(%d): error in ddns_modify_ptr %s for %p",
3002			 file, line, isc_result_totext(result), ddns_cb);
3003	}
3004#endif
3005
3006	if (dataspace != NULL) {
3007		isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
3008			    sizeof(*dataspace) * 2);
3009	}
3010	return(result);
3011}
3012
3013void
3014ddns_cancel(dhcp_ddns_cb_t *ddns_cb, const char *file, int line) {
3015	ddns_cb->flags |= DDNS_ABORT;
3016	if (ddns_cb->transaction != NULL) {
3017		dns_client_cancelupdate((dns_clientupdatetrans_t *)
3018					ddns_cb->transaction);
3019	}
3020	ddns_cb->lease = NULL;
3021
3022#if defined (DEBUG_DNS_UPDATES)
3023	log_info("DDNS: %s(%d): cancelling transaction for  %p",
3024		 file, line,  ddns_cb);
3025#endif
3026}
3027
3028#endif /* NSUPDATE */
3029
3030HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
3031		dns_zone_reference, dns_zone_dereference, do_case_hash)
3032
3033#if defined (NSUPDATE)
3034#if defined (DEBUG_DNS_UPDATES)
3035/* Defines a type for creating list of labeled integers */
3036typedef struct {
3037	int val;
3038	char *name;
3039} LabeledInt;
3040
3041char*
3042ddns_state_name(int state) {
3043	static LabeledInt ints[] = {
3044		{ DDNS_STATE_CLEANUP, "DDNS_STATE_CLEANUP" },
3045		{ DDNS_STATE_ADD_FW_NXDOMAIN, "DDNS_STATE_ADD_FW_NXDOMAIN" },
3046		{ DDNS_STATE_ADD_FW_YXDHCID, "DDNS_STATE_ADD_FW_YXDHCID" },
3047		{ DDNS_STATE_ADD_PTR, "DDNS_STATE_ADD_PTR" },
3048		{ DDNS_STATE_DSMM_FW_ADD3, "DDNS_STATE_DSMM_FW_ADD3" },
3049		{ DDNS_STATE_REM_FW_YXDHCID, "DDNS_STATE_REM_FW_YXDHCID" },
3050		{ DDNS_STATE_REM_FW_NXRR, "DDNS_STATE_FW_NXRR" },
3051		{ DDNS_STATE_REM_PTR, "DDNS_STATE_REM_PTR" },
3052		{ -1, "unknown" },
3053	};
3054
3055	LabeledInt* li = ints;
3056	while (li->val != -1 && li->val != state) {
3057		++li;
3058	}
3059
3060	return (li->name);
3061}
3062
3063int
3064add_nstring(char **orig, char *max, char *add, int add_len) {
3065	if (*orig && (*orig + add_len < max)) {
3066		strncpy(*orig, add, add_len);
3067		*orig += add_len;
3068		**orig = 0;
3069		return (0);
3070	}
3071
3072	return (-1);
3073}
3074
3075int
3076add_string(char **orig, char *max, char *add) {
3077	return (add_nstring(orig, max, add, strlen(add)));
3078}
3079
3080/*
3081 * direction outbound (messages to the dns server)
3082 *           inbound  (messages from the dns server)
3083 * ddns_cb is the control block associated with the message
3084 * result is the result from the dns code.  For outbound calls
3085 * it is from the call to pass the message to the dns library.
3086 * For inbound calls it is from the event returned by the library.
3087 *
3088 * For outbound messages we print whatever we think is interesting
3089 * from the control block.
3090 * For inbound messages we only print the transaction id pointer
3091 * and the result and expect that the user will match them up as
3092 * necessary.  Note well: the transaction information is opaque to
3093 * us so we simply print the pointer to it.  This should be sufficient
3094 * to match requests and replys in a short sequence but is awkward
3095 * when trying to use it for longer sequences.
3096 */
3097void
3098print_dns_status (int direction,
3099		  struct dhcp_ddns_cb *ddns_cb,
3100		  isc_result_t result)
3101{
3102	char obuf[1024];
3103	char *s = obuf, *end = &obuf[sizeof(obuf)-2];
3104	char *en;
3105	const char *result_str;
3106	char ddns_address[
3107		sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
3108
3109	if (direction == DDNS_PRINT_INBOUND) {
3110		log_info("DDNS reply: id ptr %p, result: %s",
3111			 ddns_cb->transaction, isc_result_totext(result));
3112		return;
3113	}
3114
3115	/*
3116	 * To avoid having to figure out if any of the strings
3117	 * aren't NULL terminated, just 0 the whole string
3118	 */
3119	memset(obuf, 0, 1024);
3120
3121	en = "DDNS request: id ptr ";
3122	if (s + strlen(en) + 16 < end) {
3123		sprintf(s, "%s%p", en, ddns_cb->transaction);
3124		s += strlen(s);
3125	} else {
3126		goto bailout;
3127	}
3128
3129	en = ddns_state_name(ddns_cb->state);
3130
3131	switch (ddns_cb->state) {
3132	case DDNS_STATE_ADD_FW_NXDOMAIN:
3133	case DDNS_STATE_ADD_FW_YXDHCID:
3134	case DDNS_STATE_REM_FW_YXDHCID:
3135	case DDNS_STATE_REM_FW_NXRR:
3136	case DDNS_STATE_DSMM_FW_ADD3:
3137		strcpy(ddns_address, piaddr(ddns_cb->address));
3138		if (s + strlen(en) + strlen(ddns_address) +
3139		    ddns_cb->fwd_name.len + 7 < end) {
3140			sprintf(s, " %s %s for %.*s", en, ddns_address,
3141				ddns_cb->fwd_name.len,
3142				ddns_cb->fwd_name.data);
3143			s += strlen(s);
3144		} else {
3145			goto bailout;
3146		}
3147		break;
3148
3149	case DDNS_STATE_ADD_PTR:
3150	case DDNS_STATE_REM_PTR:
3151		if (s + strlen(en) + ddns_cb->fwd_name.len +
3152		    ddns_cb->rev_name.len + 7 < end) {
3153			sprintf(s, " %s %.*s for %.*s", en,
3154				ddns_cb->fwd_name.len,
3155				ddns_cb->fwd_name.data,
3156				ddns_cb->rev_name.len,
3157				ddns_cb->rev_name.data);
3158			s += strlen(s);
3159		} else {
3160			goto bailout;
3161		}
3162		break;
3163
3164	case DDNS_STATE_CLEANUP:
3165	default:
3166		if (s + strlen(en) < end) {
3167			sprintf(s, "%s", en);
3168			s += strlen(s);
3169		} else {
3170			goto bailout;
3171		}
3172		break;
3173	}
3174
3175	en = " zone: ";
3176	if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
3177		sprintf(s, "%s%s", en, ddns_cb->zone_name);
3178		s += strlen(s);
3179	} else {
3180		goto bailout;
3181	}
3182
3183	/* @todo replace with format that matches bind9 zone file */
3184	if (ddns_cb->dhcid_class == dns_rdatatype_dhcid) {
3185		char *idbuf = NULL;
3186		if (add_string(&s, end, "dhcid: [")) {
3187			goto bailout;
3188		}
3189
3190		idbuf = buf_to_hex(ddns_cb->dhcid.data,
3191				   ddns_cb->dhcid.len, MDL);
3192		if (idbuf) {
3193			int ret = add_string(&s, end, idbuf);
3194			dfree(idbuf, MDL);
3195			if (!ret) {
3196				goto bailout;
3197			}
3198		}
3199
3200		if (add_string(&s, end, "]")) {
3201			goto bailout;
3202		}
3203	} else {
3204		/* 1st byte of a txt dhcid is length, so we skip printing it
3205		 * In the event it's empty, we end up not adding anything */
3206		int skip_length_byte = (ddns_cb->dhcid.len > 0 ? 1 : 0);
3207		if (add_string (&s, end, "txt: [") ||
3208		    add_nstring (&s, end,
3209				(char *)ddns_cb->dhcid.data + skip_length_byte,
3210				 ddns_cb->dhcid.len - skip_length_byte) ||
3211		    add_string (&s, end, "]")) {
3212			goto bailout;
3213		}
3214	}
3215
3216	en = " ttl: ";
3217	if (s + strlen(en) + 10 < end) {
3218		sprintf(s, "%s%ld", en, ddns_cb->ttl);
3219		s += strlen(s);
3220	} else {
3221		goto bailout;
3222	}
3223
3224	en = " result: ";
3225	result_str = isc_result_totext(result);
3226	if (s + strlen(en) + strlen(result_str) < end) {
3227		sprintf(s, "%s%s", en, result_str);
3228		s += strlen(s);
3229	} else {
3230		goto bailout;
3231	}
3232
3233 bailout:
3234	/*
3235	 * We either finished building the string or ran out
3236	 * of space, print whatever we have in case it is useful
3237	 */
3238	log_info("%s", obuf);
3239
3240	return;
3241}
3242#endif /* DEBUG_DNS_UPDATES */
3243#endif /* NSUPDATE */
3244