xref: /netbsd-src/external/mpl/dhcp/dist/common/dns.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: dns.c,v 1.5 2022/04/03 01:10:58 christos Exp $	*/
2 
3 /* dns.c
4 
5    Domain Name Service subroutines. */
6 
7 /*
8  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 2001-2003 by Internet Software Consortium
10  *
11  * This Source Code Form is subject to the terms of the Mozilla Public
12  * License, v. 2.0. If a copy of the MPL was not distributed with this
13  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  *   Internet Systems Consortium, Inc.
24  *   PO Box 360
25  *   Newmarket, NH 03857 USA
26  *   <info@isc.org>
27  *   https://www.isc.org/
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: dns.c,v 1.5 2022/04/03 01:10:58 christos Exp $");
33 
34 /*! \file common/dns.c
35  */
36 #include "dhcpd.h"
37 #include "arpa/nameser.h"
38 #include <isc/md.h>
39 #include <dns/result.h>
40 
41 /*
42  * This file contains code to connect the DHCP code to the libdns modules.
43  * As part of that function it maintains a database of zone cuts that can
44  * be used to figure out which server should be contacted to update any
45  * given domain name.  Included in the zone information may be a pointer
46  * to a key in which case that key is used for the update.  If no zone
47  * is found then the DNS code determines the zone on its own.
48  *
49  * The way this works is that you define the domain name to which an
50  * SOA corresponds, and the addresses of some primaries for that domain name:
51  *
52  *	zone FOO.COM {
53  *	  primary 10.0.17.1;
54  *	  secondary 10.0.22.1, 10.0.23.1;
55  *	  key "FOO.COM Key";
56  * 	}
57  *
58  * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
59  * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
60  * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
61  * looks for "FOO.COM", finds it. So it
62  * attempts the update to the primary for FOO.COM.   If that times out, it
63  * tries the secondaries.   You can list multiple primaries if you have some
64  * kind of magic name server that supports that.   You shouldn't list
65  * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
66  * support update forwarding, AFAIK).   If no TSIG key is listed, the update
67  * is attempted without TSIG.
68  *
69  * You can also include IPv6 addresses via the primary6 and secondary6
70  * options.  The search order for the addresses is primary, primary6,
71  * secondary and lastly secondary6, with a limit on the number of
72  * addresses used.  Currently this limit is 3.
73  *
74  * The DHCP server tries to find an existing zone for any given name by
75  * trying to look up a local zone structure for each domain containing
76  * that name, all the way up to '.'.   If it finds one cached, it tries
77  * to use that one to do the update.   That's why it tries to update
78  * "FOO.COM" above, even though theoretically it should try GAZANGA...
79  * and TOPANGA... first.
80  *
81  * If the update fails with a predefined zone the zone is marked as bad
82  * and another search of the predefined zones is done.  If no predefined
83  * zone is found finding a zone is left to the DNS module via examination
84  * of SOA records.  If the DNS module finds a zone it may cache the zone
85  * but the zone won't be cached here.
86  *
87  * TSIG updates are not performed on zones found by the DNS module - if
88  * you want TSIG updates you _must_ write a zone definition linking the
89  * key to the zone.   In cases where you know for sure what the key is
90  * but do not want to hardcode the IP addresses of the primary or
91  * secondaries, a zone declaration can be made that doesn't include any
92  * primary or secondary declarations.   When the DHCP server encounters
93  * this while hunting up a matching zone for a name, it looks up the SOA,
94  * fills in the IP addresses, and uses that record for the update.
95  * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
96  * discarded, TSIG key and all.   The search for the zone then continues
97  * as if the zone record hadn't been found.   Zones without IP addresses
98  * don't match when initially hunting for a zone to update.
99  *
100  * When an update is attempted and no predefined zone is found
101  * that matches any enclosing domain of the domain being updated, the DHCP
102  * server goes through the same process that is done when the update to a
103  * predefined zone fails - starting with the most specific domain
104  * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
105  * it tries to look up an SOA record.
106  *
107  * TSIG keys are defined like this:
108  *
109  *	key "FOO.COM Key" {
110  *		algorithm HMAC-MD5.SIG-ALG.REG.INT;
111  *		secret <Base64>;
112  *	}
113  *
114  * <Base64> is a number expressed in base64 that represents the key.
115  * It's also permissible to use a quoted string here - this will be
116  * translated as the ASCII bytes making up the string, and will not
117  * include any NUL termination.  The key name can be any text string,
118  * and the key type must be one of the key types defined in the draft
119  * or by the IANA.  Currently only the HMAC-MD5... key type is
120  * supported.
121  *
122  * The DDNS processing has been split into two areas.  One is the
123  * control code that determines what should be done.  That code is found
124  * in the client or server directories.  The other is the common code
125  * that performs functions such as properly formatting the arguments.
126  * That code is found in this file.  The basic processing flow for a
127  * DDNS update is:
128  * In the client or server code determine what needs to be done and
129  * collect the necesary information then pass it to a function from
130  * this file.
131  * In this code lookup the zone and extract the zone and key information
132  * (if available) and prepare the arguments for the DNS module.
133  * When the DNS module completes its work (times out or gets a reply)
134  * it will trigger another function here which does generic processing
135  * and then passes control back to the code from the server or client.
136  * The server or client code then determines the next step which may
137  * result in another call to this module in which case the process repeats.
138  */
139 
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
add_to_ns_queue(dhcp_ddns_ns_t * ns_cb)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
remove_from_ns_queue(dhcp_ddns_ns_t * ns_cb)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
find_in_ns_queue(dhcp_ddns_ns_t * ns_cb)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
trace_ddns_input_write(dhcp_ddns_cb_t * ddns_cb,isc_result_t result)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
trace_ddns_input_read(trace_type_t * ttype,unsigned length,char * buf)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
trace_ddns_input_stop(trace_type_t * ttype)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
trace_ddns_output_write(dns_client_t * client,dns_rdataclass_t rdclass,dns_name_t * zonename,dns_namelist_t * prerequisites,dns_namelist_t * updates,isc_sockaddrlist_t * servers,dns_tsec_t * tsec,unsigned int options,isc_task_t * task,isc_taskaction_t action,void * arg,dns_clientupdatetrans_t ** transp)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
trace_ddns_output_read(trace_type_t * ttype,unsigned length,char * buf)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
trace_ddns_output_stop(trace_type_t * ttype)537 trace_ddns_output_stop(trace_type_t *ttype)
538 {
539 }
540 
541 void
trace_ddns_init()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 *
ddns_cb_alloc(const char * file,int line)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
ddns_cb_free(dhcp_ddns_cb_t * ddns_cb,const char * file,int line)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
ddns_cb_forget_zone(dhcp_ddns_cb_t * ddns_cb)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 
remove_dns_zone(struct dns_zone * zone)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 
enter_dns_zone(struct dns_zone * zone)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 
dns_zone_lookup(struct dns_zone ** zone,const char * name)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 
dns_zone_dereference(ptr,file,line)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
zone_addr_to_ns(dhcp_ddns_ns_t * ns_cb,dns_rdataset_t * rdataset)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
find_zone_addrs(isc_task_t * taskp,isc_event_t * eventp)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
find_zone_ns(isc_task_t * taskp,isc_event_t * eventp)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
find_zone_start(dhcp_ddns_cb_t * ddns_cb,int direction)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
find_cached_zone(dhcp_ddns_cb_t * ddns_cb,int direction)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 
forget_zone(struct dns_zone ** zone)1355 void forget_zone (struct dns_zone **zone)
1356 {
1357 	dns_zone_dereference (zone, MDL);
1358 }
1359 
repudiate_zone(struct dns_zone ** zone)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)
cache_found_zone(dhcp_ddns_ns_t * ns_cb)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  */
get_std_dhcid(dhcp_ddns_cb_t * ddns_cb,int type,const u_int8_t * identifier,unsigned id_len)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 
get_int_dhcid(dhcp_ddns_cb_t * ddns_cb,int type,const u_int8_t * data,unsigned len)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 
get_dhcid(dhcp_ddns_cb_t * ddns_cb,int type,const u_int8_t * identifier,unsigned id_len)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
dhcid_tolease(struct data_string * dhcid,struct data_string * leaseid)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
dhcid_fromlease(struct data_string * dhcid,struct data_string * leaseid)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
make_dns_dataset(dns_rdataclass_t dataclass,dns_rdatatype_t datatype,dhcp_ddns_data_t * dataspace,unsigned char * data,int datalen,int ttl)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)
log_call(char * text,dns_name_t * pname,dns_name_t * uname)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
build_fwd_add1(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_fwd_add2(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_dsmm_fwd_add3(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_dsmm_fwd_add3_other(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_fwd_rem1(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_fwd_rem2(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_fwd_rem2_dsmm(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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
build_fwd_rem2_dsmm_other(dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_data_t * dataspace,dns_name_t * pname,dns_name_t * uname)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  */
ddns_interlude(isc_task_t * taskp,isc_event_t * eventp)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
ddns_modify_fwd(dhcp_ddns_cb_t * ddns_cb,const char * file,int line)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
ddns_modify_ptr(dhcp_ddns_cb_t * ddns_cb,const char * file,int line)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
ddns_cancel(dhcp_ddns_cb_t * ddns_cb,const char * file,int line)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*
ddns_state_name(int state)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
add_nstring(char ** orig,char * max,char * add,int add_len)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
add_string(char ** orig,char * max,char * add)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
print_dns_status(int direction,struct dhcp_ddns_cb * ddns_cb,isc_result_t result)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