Home | History | Annotate | Line # | Download | only in common
      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 (at) 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 
    140 dns_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 
    153 typedef 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 */
    160 typedef 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 
    179 typedef 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  */
    215 dhcp_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 
    232 static void
    233 add_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 
    240 static void
    241 remove_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 
    256 static isc_result_t
    257 find_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 
    277 void cache_found_zone (dhcp_ddns_ns_t *);
    278 #endif
    279 
    280 void 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 
    320 typedef 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 */
    327 static dhcp_ddns_map_t *ddns_map;
    328 
    329 trace_type_t *trace_ddns_input;
    330 trace_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 
    338 static void
    339 trace_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  */
    362 static void
    363 trace_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 
    417 static void
    418 trace_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 
    439 static isc_result_t
    440 trace_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 
    529 static void
    530 trace_ddns_output_read(trace_type_t *ttype, unsigned length,
    531 				   char *buf)
    532 {
    533 	log_error("unaccounted for ddns output.");
    534 }
    535 
    536 static void
    537 trace_ddns_output_stop(trace_type_t *ttype)
    538 {
    539 }
    540 
    541 void
    542 trace_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  */
    564 dhcp_ddns_cb_t *
    565 ddns_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 
    585 void
    586 ddns_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 
    615 void
    616 ddns_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 
    629 static 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 
    644 isc_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 
    669 isc_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 
    702 int 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  */
    760 static void
    761 zone_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  */
    822 static void
    823 find_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 
    990 static void
    991 find_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  */
   1134 static isc_result_t
   1135 find_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 
   1204 isc_result_t
   1205 find_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 
   1355 void forget_zone (struct dns_zone **zone)
   1356 {
   1357 	dns_zone_dereference (zone, MDL);
   1358 }
   1359 
   1360 void 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)
   1373 void 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  */
   1479 static 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);
   1550 end:
   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 
   1578 static 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);
   1654 end:
   1655 	if (md != NULL) {
   1656 		isc_md_free(md);
   1657 	}
   1658 	return (0);
   1659 }
   1660 
   1661 int 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 
   1683 void
   1684 dhcid_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 
   1693 isc_result_t
   1694 dhcid_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 
   1722 static isc_result_t
   1723 make_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)
   1779 static 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 
   1820 static isc_result_t
   1821 build_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 
   1913 static isc_result_t
   1914 build_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  */
   2030 static isc_result_t
   2031 build_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  */
   2124 static isc_result_t
   2125 build_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  */
   2217 static isc_result_t
   2218 build_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  */
   2284 static isc_result_t
   2285 build_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  */
   2356 static isc_result_t
   2357 build_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  */
   2419 static isc_result_t
   2420 build_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  */
   2470 void 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 
   2568 isc_result_t
   2569 ddns_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 
   2820 isc_result_t
   2821 ddns_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 
   3013 void
   3014 ddns_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 
   3030 HASH_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 */
   3036 typedef struct {
   3037 	int val;
   3038 	char *name;
   3039 } LabeledInt;
   3040 
   3041 char*
   3042 ddns_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 
   3063 int
   3064 add_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 
   3075 int
   3076 add_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  */
   3097 void
   3098 print_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