xref: /netbsd/external/mpl/dhcp/dist/server/ddns.c (revision 13df4856)
1 /*	$NetBSD: ddns.c,v 1.4 2022/04/03 01:10:59 christos Exp $	*/
2 
3 /* ddns.c
4 
5    Dynamic DNS updates. */
6 
7 /*
8  *
9  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
10  * Copyright (c) 2000-2003 by Internet Software Consortium
11  *
12  * This Source Code Form is subject to the terms of the Mozilla Public
13  * License, v. 2.0. If a copy of the MPL was not distributed with this
14  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
22  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  *   Internet Systems Consortium, Inc.
25  *   PO Box 360
26  *   Newmarket, NH 03857 USA
27  *   <info@isc.org>
28  *   https://www.isc.org/
29  *
30  * This software has been donated to Internet Systems Consortium
31  * by Damien Neil of Nominum, Inc.
32  *
33  * To learn more about Internet Systems Consortium, see
34  * ``https://www.isc.org/''.
35  */
36 
37 #include <sys/cdefs.h>
38 __RCSID("$NetBSD: ddns.c,v 1.4 2022/04/03 01:10:59 christos Exp $");
39 
40 #include "dhcpd.h"
41 #include <dns/result.h>
42 
43 char *ddns_standard_tag = "ddns-dhcid";
44 char *ddns_interim_tag  = "ddns-txt";
45 
46 #ifdef NSUPDATE
47 
48 #if defined (DEBUG_DNS_UPDATES)
49 static char* dump_ddns_cb_func(void *func);
50 static char* dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb);
51 
52 extern struct enumeration_value ddns_styles_values[];
53 #endif
54 
55 static void ddns_fwd_srv_connector(struct lease          *lease,
56 				   struct iasubopt       *lease6,
57 				   struct binding_scope **inscope,
58 				   dhcp_ddns_cb_t        *ddns_cb,
59 				   isc_result_t           eresult);
60 
61 static void copy_conflict_flags(u_int16_t *target, u_int16_t source);
62 
63 static void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult);
64 
65 /*
66  * ddns_cb_free() is part of common lib, while ia_* routines are known
67  * only in the server.  Use this wrapper instead of ddns_cb_free() directly.
68  */
69 static void
destroy_ddns_cb(struct dhcp_ddns_cb * ddns_cb,char * file,int line)70 destroy_ddns_cb(struct dhcp_ddns_cb *ddns_cb, char* file, int line) {
71 	if (!ddns_cb) {
72 		return;
73 	}
74 
75         if (ddns_cb->fixed6_ia) {
76                 ia_dereference(&ddns_cb->fixed6_ia, MDL);
77         }
78 
79 	ddns_cb_free(ddns_cb, file, line);
80 
81 }
82 
83 
84 /* DN: No way of checking that there is enough space in a data_string's
85    buffer.  Be certain to allocate enough!
86    TL: This is why the expression evaluation code allocates a *new*
87    data_string.   :') */
data_string_append(struct data_string * ds1,struct data_string * ds2)88 static void data_string_append (struct data_string *ds1,
89 				struct data_string *ds2)
90 {
91 	memcpy (ds1 -> buffer -> data + ds1 -> len,
92 		ds2 -> data,
93 		ds2 -> len);
94 	ds1 -> len += ds2 -> len;
95 }
96 
97 
98 /* Determine what, if any, forward and reverse updates need to be
99  * performed, and carry them through.
100  */
101 int
ddns_updates(struct packet * packet,struct lease * lease,struct lease * old,struct iasubopt * lease6,struct iasubopt * old6,struct option_state * options)102 ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
103 	     struct iasubopt *lease6, struct iasubopt *old6,
104 	     struct option_state *options)
105 {
106 	unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
107 	struct data_string ddns_hostname;
108 	struct data_string ddns_domainname;
109 	struct data_string old_ddns_fwd_name;
110 	struct data_string ddns_fwd_name;
111 	struct data_string ddns_dhcid;
112 	struct binding_scope **scope = NULL;
113 	struct data_string d1;
114 	struct option_cache *oc;
115 	int s1, s2;
116 	int result = 0;
117 	int server_updates_a = 1;
118 	struct buffer *bp = (struct buffer *)0;
119 	int ignorep = 0, client_ignorep = 0;
120 	int rev_name_len;
121 	int i;
122 
123 	dhcp_ddns_cb_t *ddns_cb;
124 	int do_remove = 0;
125 
126 	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
127 	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
128 		return (0);
129 
130 	/*
131 	 * sigh, I want to cancel any previous udpates before we do anything
132 	 * else but this means we need to deal with the lease vs lease6
133 	 * question twice.
134 	 * If there is a ddns request already outstanding cancel it.
135 	 */
136 
137 	if (lease != NULL) {
138 		if ((old != NULL) && (old->ddns_cb != NULL)) {
139 			ddns_cancel(old->ddns_cb, MDL);
140 			old->ddns_cb = NULL;
141 		}
142 	} else if (lease6 != NULL) {
143 		if ((old6 != NULL) && (old6->ddns_cb != NULL)) {
144 			ddns_cancel(old6->ddns_cb, MDL);
145 			old6->ddns_cb = NULL;
146 		}
147 	} else {
148 		log_fatal("Impossible condition at %s:%d.", MDL);
149 		/* Silence compiler warnings. */
150 		result = 0;
151 		return(0);
152 	}
153 
154 	/* allocate our control block */
155 	ddns_cb = ddns_cb_alloc(MDL);
156 	if (ddns_cb == NULL) {
157 		return(0);
158 	}
159 	/*
160 	 * Assume that we shall update both the A and ptr records and,
161 	 * as this is an update, set the active flag
162 	 */
163 	ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR |
164 		DDNS_ACTIVE_LEASE;
165 
166 	/*
167 	 * For v4 we flag static leases so we don't try
168 	 * and manipulate the lease later.  For v6 we don't
169 	 * get static leases and don't need to flag them.
170 	 */
171 	if (lease != NULL) {
172 		scope = &(lease->scope);
173 		ddns_cb->address = lease->ip_addr;
174 		if (lease->flags & STATIC_LEASE)
175 			ddns_cb->flags |= DDNS_STATIC_LEASE;
176 	} else if (lease6 != NULL) {
177 		scope = &(lease6->scope);
178 		memcpy(ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
179 		ddns_cb->address.len = 16;
180 
181 		if (lease6->static_lease) {
182 			/* We add a reference to keep ia && iasubopt alive
183 			* since static v6s are retained anywhere */
184 			ia_reference(&ddns_cb->fixed6_ia, lease6->ia, MDL);
185 			ddns_cb->flags |= DDNS_STATIC_LEASE;
186 		}
187 	}
188 
189 	memset (&d1, 0, sizeof(d1));
190 	memset (&ddns_hostname, 0, sizeof (ddns_hostname));
191 	memset (&ddns_domainname, 0, sizeof (ddns_domainname));
192 	memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
193 	memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
194 	memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
195 
196 	/* If we are allowed to accept the client's update of its own A
197 	   record, see if the client wants to update its own A record. */
198 	if (!(oc = lookup_option(&server_universe, options,
199 				 SV_CLIENT_UPDATES)) ||
200 	    evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
201 					  packet->options, options, scope,
202 					  oc, MDL)) {
203 		/* If there's no fqdn.no-client-update or if it's
204 		   nonzero, don't try to use the client-supplied
205 		   XXX */
206 		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
207 					  FQDN_SERVER_UPDATE)) ||
208 		    evaluate_boolean_option_cache(&ignorep, packet, lease,
209 						  NULL, packet->options,
210 						  options, scope, oc, MDL))
211 			goto noclient;
212 		/* Win98 and Win2k will happily claim to be willing to
213 		   update an unqualified domain name. */
214 		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
215 					  FQDN_DOMAINNAME)))
216 			goto noclient;
217 		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
218 					  FQDN_FQDN)) ||
219 		    !evaluate_option_cache(&ddns_fwd_name, packet, lease,
220 					   NULL, packet->options,
221 					   options, scope, oc, MDL))
222 			goto noclient;
223 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
224 		server_updates_a = 0;
225 		goto client_updates;
226 	}
227       noclient:
228 	/* If do-forward-updates is disabled, this basically means don't
229 	   do an update unless the client is participating, so if we get
230 	   here and do-forward-updates is disabled, we can stop. */
231 	if ((oc = lookup_option (&server_universe, options,
232 				 SV_DO_FORWARD_UPDATES)) &&
233 	    !evaluate_boolean_option_cache(&ignorep, packet, lease,
234 					   NULL, packet->options,
235 					   options, scope, oc, MDL)) {
236 		goto out;
237 	}
238 
239 	/* If it's a static lease, then don't do the DNS update unless we're
240 	   specifically configured to do so.   If the client asked to do its
241 	   own update and we allowed that, we don't do this test. */
242 	/* XXX: note that we cannot detect static DHCPv6 leases. */
243 	if ((lease != NULL) && (lease->flags & STATIC_LEASE)) {
244 		if (!(oc = lookup_option(&server_universe, options,
245 					 SV_UPDATE_STATIC_LEASES)) ||
246 		    !evaluate_boolean_option_cache(&ignorep, packet, lease,
247 						   NULL, packet->options,
248 						   options, scope, oc, MDL))
249 			goto out;
250 	}
251 
252 	/*
253 	 * Compute the name for the A record.
254 	 */
255 	oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
256 	if (oc)
257 		s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
258 					   NULL, packet->options,
259 					   options, scope, oc, MDL);
260 	else
261 		s1 = 0;
262 
263 	/* If we don't have a host name based on ddns-hostname then use
264 	 * the host declaration name if there is one and use-host-decl-names
265 	 * is turned on. */
266 	if ((s1 == 0) && (lease && lease->host && lease->host->name)) {
267 		oc = lookup_option(&server_universe, options,
268 				   SV_USE_HOST_DECL_NAMES);
269 		if (evaluate_boolean_option_cache(NULL, packet, lease,
270 						  NULL, packet->options,
271 						  options, scope, oc, MDL)) {
272 			s1 = ((data_string_new(&ddns_hostname,
273 					      lease->host->name,
274 					      strlen(lease->host->name),
275                                               MDL) && ddns_hostname.len > 0));
276 		}
277 	}
278 
279 	oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
280 	if (oc)
281 		s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
282 					   NULL, packet->options,
283 					   options, scope, oc, MDL);
284 	else
285 		s2 = 0;
286 
287 	if (s1 && s2) {
288 		if (ddns_hostname.len + ddns_domainname.len > 253) {
289 			log_error ("ddns_update: host.domain name too long");
290 
291 			goto out;
292 		}
293 
294 		if (buffer_allocate (&ddns_fwd_name.buffer,
295 				     ddns_hostname.len +
296 				     ddns_domainname.len + 2, MDL)) {
297 			ddns_fwd_name.data = ddns_fwd_name.buffer->data;
298 			data_string_append (&ddns_fwd_name, &ddns_hostname);
299 			ddns_fwd_name.buffer->data[ddns_fwd_name.len] = '.';
300 			ddns_fwd_name.len++;
301 			data_string_append (&ddns_fwd_name, &ddns_domainname);
302 			ddns_fwd_name.buffer->data[ddns_fwd_name.len] ='\0';
303 			ddns_fwd_name.terminated = 1;
304 		}
305 	}
306       client_updates:
307 
308 	/* See if there's a name already stored on the lease. */
309 	if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) {
310 		/* If there is, see if it's different. */
311 		if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
312 		    memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
313 			    old_ddns_fwd_name.len)) {
314 			/*
315 			 * If the name is different, mark the old record
316 			 * for deletion and continue getting the new info.
317 			 */
318 			do_remove = 1;
319 			goto in;
320 		}
321 
322 #if defined  (DDNS_UPDATE_SLOW_TRANSITION)
323 		/*
324 		 * If the slow transition code is enabled check to see
325 		 * if the stored type (standard or interim doesn't
326 		 * match the type currently in use.  If it doesn't
327 		 * try to remove and replace the DNS record
328 		 */
329 		if (((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
330 		     find_bound_string(&ddns_dhcid, *scope, ddns_interim_tag)) ||
331 		    ((ddns_update_style == DDNS_UPDATE_STYLE_INTERIM) &&
332 		     find_bound_string(&ddns_dhcid, *scope, ddns_standard_tag))) {
333 			data_string_forget(&ddns_dhcid, MDL);
334 			do_remove = 1;
335 			goto in;
336 		}
337 #endif
338 
339 		/* See if the administrator wants to do updates even
340 		   in cases where the update already appears to have been
341 		   done. */
342 		if (!(oc = lookup_option(&server_universe, options,
343 					 SV_UPDATE_OPTIMIZATION)) ||
344 		    evaluate_boolean_option_cache(&ignorep, packet, lease,
345 						  NULL, packet->options,
346 						  options, scope, oc, MDL)) {
347 			result = 1;
348 			goto noerror;
349 		}
350 	/* If there's no "ddns-fwd-name" on the lease record, see if
351 	 * there's a ddns-client-fqdn indicating a previous client
352 	 * update (if it changes, we need to adjust the PTR).
353 	 */
354 	} else if (find_bound_string(&old_ddns_fwd_name, *scope,
355 				     "ddns-client-fqdn")) {
356 		/* If the name is not different, no need to update
357 		   the PTR record. */
358 		if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
359 		    !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
360 			     old_ddns_fwd_name.len) &&
361 		    (!(oc = lookup_option(&server_universe, options,
362 					  SV_UPDATE_OPTIMIZATION)) ||
363 		     evaluate_boolean_option_cache(&ignorep, packet, lease,
364 						   NULL, packet->options,
365 						   options, scope, oc, MDL))) {
366 			goto noerror;
367 		}
368 	}
369       in:
370 
371 	/* If we don't have a name that the client has been assigned, we
372 	   can just skip all this. */
373 
374 	if ((!ddns_fwd_name.len) || (ddns_fwd_name.len > 255)) {
375 		if (ddns_fwd_name.len > 255) {
376 			log_error ("client provided fqdn: too long");
377 		}
378 
379 		/* If desired do the removals */
380 		if (do_remove != 0) {
381 			(void) ddns_removals(lease, lease6, NULL, ISC_TRUE);
382 		}
383 		goto out;
384 	}
385 
386 	/*
387 	 * Compute the RR TTL.
388 	 *
389 	 * We have two ways of computing the TTL.
390 	 * The old behavior was to allow for the customer to set up
391 	 * the option or to default things.  For v4 this was 1/2
392 	 * of the lease time, for v6 this was DEFAULT_DDNS_TTL.
393 	 * The new behavior continues to allow the customer to set
394 	 * up an option but the defaults are a little different.
395 	 * We now use 1/2 of the (preferred) lease time for both
396 	 * v4 and v6 and cap them at a maximum value.
397 	 * If the customer chooses to use an experession that references
398 	 * part of the lease the v6 value will be the default as there
399 	 * isn't a lease available for v6.
400 	 */
401 
402 	ddns_ttl = DEFAULT_DDNS_TTL;
403 	if (lease != NULL) {
404 		if (lease->ends <= cur_time) {
405 			ddns_ttl = 0;
406 		} else {
407 			ddns_ttl = (lease->ends - cur_time)/2;
408 		}
409 	}
410 #ifndef USE_OLD_DDNS_TTL
411 	else if (lease6 != NULL) {
412 		ddns_ttl = lease6->prefer/2;
413 	}
414 
415 	if (ddns_ttl > MAX_DEFAULT_DDNS_TTL) {
416 		ddns_ttl = MAX_DEFAULT_DDNS_TTL;
417 	}
418 #endif
419 
420 	if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) {
421 		if (evaluate_option_cache(&d1, packet, lease, NULL,
422 					  packet->options, options,
423 					  scope, oc, MDL)) {
424 			if (d1.len == sizeof (u_int32_t))
425 				ddns_ttl = getULong (d1.data);
426 			data_string_forget (&d1, MDL);
427 		}
428 	}
429 
430 	ddns_cb->ttl = ddns_ttl;
431 
432 	/*
433 	 * Compute the reverse IP name, starting with the domain name.
434 	 */
435 	oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
436 	if (oc)
437 		s1 = evaluate_option_cache(&d1, packet, lease, NULL,
438 					   packet->options, options,
439 					   scope, oc, MDL);
440 	else
441 		s1 = 0;
442 
443 	/*
444 	 * Figure out the length of the part of the name that depends
445 	 * on the address.
446 	 */
447 	if (ddns_cb->address.len == 4) {
448 		char buf[17];
449 		/* XXX: WOW this is gross. */
450 		rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
451 					ddns_cb->address.iabuf[3] & 0xff,
452 					ddns_cb->address.iabuf[2] & 0xff,
453 					ddns_cb->address.iabuf[1] & 0xff,
454 					ddns_cb->address.iabuf[0] & 0xff) + 1;
455 
456 		if (s1) {
457 			rev_name_len += d1.len;
458 
459 			if (rev_name_len > 255) {
460 				log_error("ddns_update: Calculated rev domain "
461 					  "name too long.");
462 				s1 = 0;
463 				data_string_forget(&d1, MDL);
464 			}
465 		}
466 	} else if (ddns_cb->address.len == 16) {
467 		/*
468 		 * IPv6 reverse names are always the same length, with
469 		 * 32 hex characters separated by dots.
470 		 */
471 		rev_name_len = sizeof("0.1.2.3.4.5.6.7."
472 				      "8.9.a.b.c.d.e.f."
473 				      "0.1.2.3.4.5.6.7."
474 				      "8.9.a.b.c.d.e.f."
475 				      "ip6.arpa.");
476 
477 		/* Set s1 to make sure we gate into updates. */
478 		s1 = 1;
479 	} else {
480 		log_fatal("invalid address length %d", ddns_cb->address.len);
481 		/* Silence compiler warnings. */
482 		return 0;
483 	}
484 
485 	/* See if we are configured NOT to do reverse ptr updates */
486 	if ((oc = lookup_option(&server_universe, options,
487 				SV_DO_REVERSE_UPDATES)) &&
488 	    !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
489 					   packet->options, options,
490 					   scope, oc, MDL)) {
491 		ddns_cb->flags &= ~DDNS_UPDATE_PTR;
492 	}
493 
494 	if (s1) {
495 		if (buffer_allocate(&ddns_cb->rev_name.buffer,
496 				    rev_name_len, MDL)) {
497 			struct data_string *rname = &ddns_cb->rev_name;
498 			rname->data = rname->buffer->data;
499 
500 			if (ddns_cb->address.len == 4) {
501 				rname->len =
502 				    sprintf((char *)rname->buffer->data,
503 					    "%u.%u.%u.%u.",
504 					    ddns_cb->address.iabuf[3] & 0xff,
505 					    ddns_cb->address.iabuf[2] & 0xff,
506 					    ddns_cb->address.iabuf[1] & 0xff,
507 					    ddns_cb->address.iabuf[0] & 0xff);
508 
509 				/*
510 				 * d1.data may be opaque, garbage bytes, from
511 				 * user (mis)configuration.
512 				 */
513 				data_string_append(rname, &d1);
514 				rname->buffer->data[rname->len] = '\0';
515 			} else if (ddns_cb->address.len == 16) {
516 				char *p = (char *)&rname->buffer->data;
517 				unsigned char *a = ddns_cb->address.iabuf + 15;
518 				for (i=0; i<16; i++) {
519 					sprintf(p, "%x.%x.",
520 						(*a & 0xF), ((*a >> 4) & 0xF));
521 					p += 4;
522 					a -= 1;
523 				}
524 				strcat(p, "ip6.arpa.");
525 				rname->len = strlen((const char *)rname->data);
526 			}
527 
528 			rname->terminated = 1;
529 		}
530 
531 		if (d1.data != NULL)
532 			data_string_forget(&d1, MDL);
533 	}
534 
535 	/*
536 	 * copy the string now so we can pass it to the dhcid routines
537 	 * via the ddns_cb pointer
538 	 */
539 	data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL);
540 
541 	/*
542 	 * If we are updating the A record, compute the DHCID value.
543 	 * We have two options for computing the DHCID value, the older
544 	 * interim version and the newer standard version.  The interim
545 	 * has some issues but is left as is to avoid compatibility issues.
546 	 *
547 	 * We select the type of DHCID to construct and the information to
548 	 * use for the digest based on 4701 section 3.3
549 	 */
550 	if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
551 		int ddns_type;
552 		int ddns_len;
553 		if (ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) {
554 			/* The standard style */
555 			ddns_cb->lease_tag = ddns_standard_tag;
556 			ddns_cb->dhcid_class = dns_rdatatype_dhcid;
557 			ddns_cb->other_dhcid_class = dns_rdatatype_txt;
558 			ddns_type = 1;
559 			ddns_len = 4;
560 		} else {
561 			/* The older interim style */
562 			ddns_cb->lease_tag = ddns_interim_tag;
563 			ddns_cb->dhcid_class = dns_rdatatype_txt;
564 			ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
565 			/* for backwards compatibility */
566 			ddns_type = DHO_DHCP_CLIENT_IDENTIFIER;
567 			/* IAID incorrectly included */
568 			ddns_len = 0;
569 		}
570 
571 
572 		if (lease6 != NULL) {
573 			if (lease6->ia->iaid_duid.len < ddns_len)
574 				goto badfqdn;
575 			result = get_dhcid(ddns_cb, 2,
576 					   lease6->ia->iaid_duid.data + ddns_len,
577 					   lease6->ia->iaid_duid.len - ddns_len);
578 		} else if ((lease != NULL) &&
579 			   (lease->uid != NULL) &&
580 			   (lease->uid_len != 0)) {
581 			/* If this is standard check for an RFC 4361
582 			 * compliant client identifier
583 			 */
584 			if ((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
585 			    (lease->uid[0] == 255)) {
586 				if (lease->uid_len < 5)
587 					goto badfqdn;
588 				result = get_dhcid(ddns_cb, 2,
589 						   lease->uid + 5,
590 						   lease->uid_len - 5);
591 			} else {
592 				result = get_dhcid(ddns_cb, ddns_type,
593 						   lease->uid,
594 						   lease->uid_len);
595 			}
596 		} else if (lease != NULL)
597 			result = get_dhcid(ddns_cb, 0,
598 					   lease->hardware_addr.hbuf,
599 					   lease->hardware_addr.hlen);
600 		else
601 			log_fatal("Impossible condition at %s:%d.", MDL);
602 
603 		if (!result)
604 			goto badfqdn;
605 	}
606 
607 	/*
608 	 * Perform updates.
609 	 */
610 
611 	if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
612 		copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
613 	}
614 
615 	/*
616 	 * Previously if we failed during the removal operations
617 	 * we skipped the fqdn option processing.  I'm not sure
618 	 * if we want to continue with that if we fail before sending
619 	 * the ddns messages.  Currently we don't.
620 	 */
621 	if (do_remove) {
622 		/*
623 		 * We should log a more specific error closer to the actual
624 		 * error if we want one. ddns_removal failure not logged here.
625 		 */
626 		 (void) ddns_removals(lease, lease6, ddns_cb, ISC_TRUE);
627 	}
628 	else {
629 		ddns_fwd_srv_connector(lease, lease6, scope, ddns_cb,
630 				       ISC_R_SUCCESS);
631 	}
632 	ddns_cb = NULL;
633 
634       noerror:
635 	/*
636 	 * If fqdn-reply option is disabled in dhcpd.conf, then don't
637 	 * send the client an FQDN option at all, even if one was requested.
638 	 * (WinXP clients allegedly misbehave if the option is present,
639 	 * refusing to handle PTR updates themselves).
640 	 */
641 	if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) &&
642   	    !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
643   					   packet->options, options,
644   					   scope, oc, MDL)) {
645   	    	goto badfqdn;
646 
647 	/* If we're ignoring client updates, then we tell a sort of 'white
648 	 * lie'.  We've already updated the name the server wants (per the
649 	 * config written by the server admin).  Now let the client do as
650 	 * it pleases with the name they supplied (if any).
651 	 *
652 	 * We only form an FQDN option this way if the client supplied an
653 	 * FQDN option that had FQDN_SERVER_UPDATE set false.
654 	 */
655 	} else if (client_ignorep &&
656 	    (oc = lookup_option(&fqdn_universe, packet->options,
657 				FQDN_SERVER_UPDATE)) &&
658 	    !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
659 					   packet->options, options,
660 					   scope, oc, MDL)) {
661 		oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN);
662 		if (oc && evaluate_option_cache(&d1, packet, lease, NULL,
663 						packet->options, options,
664 						scope, oc, MDL)) {
665 			if (d1.len == 0 ||
666 			    !buffer_allocate(&bp, d1.len + 5, MDL))
667 				goto badfqdn;
668 
669 			/* Server pretends it is not updating. */
670 			bp->data[0] = 0;
671 			if (!save_option_buffer(&fqdn_universe, options,
672 						bp, &bp->data[0], 1,
673 						FQDN_SERVER_UPDATE, 0))
674 				goto badfqdn;
675 
676 			/* Client is encouraged to update. */
677 			bp->data[1] = 0;
678 			if (!save_option_buffer(&fqdn_universe, options,
679 						bp, &bp->data[1], 1,
680 						FQDN_NO_CLIENT_UPDATE, 0))
681 				goto badfqdn;
682 
683 			/* Use the encoding of client's FQDN option. */
684 			oc = lookup_option(&fqdn_universe, packet->options,
685 					   FQDN_ENCODED);
686 			if (oc &&
687 			    evaluate_boolean_option_cache(&ignorep, packet,
688 							  lease, NULL,
689 							  packet->options,
690 							  options, scope,
691 							  oc, MDL))
692 				bp->data[2] = 1; /* FQDN is encoded. */
693 			else
694 				bp->data[2] = 0; /* FQDN is not encoded. */
695 
696 			if (!save_option_buffer(&fqdn_universe, options,
697 						bp, &bp->data[2], 1,
698 						FQDN_ENCODED, 0))
699 				goto badfqdn;
700 
701 			/* Current FQDN drafts indicate 255 is mandatory. */
702 			bp->data[3] = 255;
703 			if (!save_option_buffer(&fqdn_universe, options,
704 						bp, &bp->data[3], 1,
705 						FQDN_RCODE1, 0))
706 				goto badfqdn;
707 
708 			bp->data[4] = 255;
709 			if (!save_option_buffer(&fqdn_universe, options,
710 						bp, &bp->data[4], 1,
711 						FQDN_RCODE2, 0))
712 				goto badfqdn;
713 
714 			/* Copy in the FQDN supplied by the client.  Note well
715 			 * that the format of this option in the cache is going
716 			 * to be in text format.  If the fqdn supplied by the
717 			 * client is encoded, it is decoded into the option
718 			 * cache when parsed out of the packet.  It will be
719 			 * re-encoded when the option is assembled to be
720 			 * transmitted if the client elects that encoding.
721 			 */
722 			memcpy(&bp->data[5], d1.data, d1.len);
723 			if (!save_option_buffer(&fqdn_universe, options,
724 						bp, &bp->data[5], d1.len,
725 						FQDN_FQDN, 0))
726 				goto badfqdn;
727 
728 			data_string_forget(&d1, MDL);
729 		}
730 	/* Set up the outgoing FQDN option if there was an incoming
731 	 * FQDN option.  If there's a valid FQDN option, there MUST
732 	 * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed
733 	 * length head of the option contents, so we test the latter
734 	 * to detect the presence of the former.
735 	 */
736 	} else if ((oc = lookup_option(&fqdn_universe, packet->options,
737 				       FQDN_ENCODED)) &&
738 		   buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
739 		bp -> data [0] = server_updates_a;
740 		if (!save_option_buffer(&fqdn_universe, options,
741 					bp, &bp->data [0], 1,
742 					FQDN_SERVER_UPDATE, 0))
743 			goto badfqdn;
744 		bp -> data [1] = server_updates_a;
745 		if (!save_option_buffer(&fqdn_universe, options,
746 					 bp, &bp->data [1], 1,
747 					 FQDN_NO_CLIENT_UPDATE, 0))
748 			goto badfqdn;
749 
750 		/* Do the same encoding the client did. */
751 		if (evaluate_boolean_option_cache(&ignorep, packet, lease,
752 						  NULL, packet->options,
753 						  options, scope, oc, MDL))
754 			bp -> data [2] = 1;
755 		else
756 			bp -> data [2] = 0;
757 		if (!save_option_buffer(&fqdn_universe, options,
758 					bp, &bp->data [2], 1,
759 					FQDN_ENCODED, 0))
760 			goto badfqdn;
761 		bp -> data [3] = 255;//isc_rcode_to_ns (rcode1);
762 		if (!save_option_buffer(&fqdn_universe, options,
763 					bp, &bp->data [3], 1,
764 					FQDN_RCODE1, 0))
765 			goto badfqdn;
766 		bp -> data [4] = 255;//isc_rcode_to_ns (rcode2);
767 		if (!save_option_buffer(&fqdn_universe, options,
768 					bp, &bp->data [4], 1,
769 					FQDN_RCODE2, 0))
770 			goto badfqdn;
771 		if (ddns_fwd_name.len) {
772 		    memcpy (&bp -> data [5],
773 			    ddns_fwd_name.data, ddns_fwd_name.len);
774 		    if (!save_option_buffer(&fqdn_universe, options,
775 					     bp, &bp->data [5],
776 					     ddns_fwd_name.len,
777 					     FQDN_FQDN, 0))
778 			goto badfqdn;
779 		}
780 	}
781 
782       badfqdn:
783       out:
784 	/*
785 	 * Final cleanup.
786 	 */
787 	if (ddns_cb != NULL) {
788 		destroy_ddns_cb(ddns_cb, MDL);
789 	}
790 
791 	data_string_forget(&d1, MDL);
792 	data_string_forget(&ddns_hostname, MDL);
793 	data_string_forget(&ddns_domainname, MDL);
794 	data_string_forget(&old_ddns_fwd_name, MDL);
795 	data_string_forget(&ddns_fwd_name, MDL);
796 	if (bp)
797 		buffer_dereference(&bp, MDL);
798 
799 	return result;
800 }
801 
802 /*%<
803  * Utility function to update text strings within a lease.
804  *
805  * The first issue is to find the proper scope.  Sometimes we shall be
806  * called with a pointer to the scope in other cases we need to find
807  * the proper lease and then get the scope.  Once we have the scope we update
808  * the proper strings, as indicated by the state value in the control block.
809  * Lastly, if we needed to find the scope we write it out, if we used a
810  * scope that was passed as an argument we don't write it, assuming that
811  * our caller (or his ...) will do the write.
812  *
813  *\li ddns_cb - the control block for the DDNS request
814  *
815  *\li inscope - a pointer to the scope to update.  This may be NULL
816  *    in which case we use the control block to find the lease and
817  *    then the scope.
818  *
819  * Returns
820  *\li ISC_R_SUCCESS
821  *
822  *\li ISC_R_FAILURE - The routine was unable to find an expected scope.
823  *    In some cases (static and inactive leases) we don't expect a scope
824  *    and return success.
825  */
826 
827 static isc_result_t
ddns_update_lease_text(dhcp_ddns_cb_t * ddns_cb,struct binding_scope ** inscope)828 ddns_update_lease_text(dhcp_ddns_cb_t        *ddns_cb,
829 		       struct binding_scope **inscope)
830 {
831 	struct binding_scope **scope  = NULL;
832 	struct lease          *lease  = NULL;
833 	struct iasubopt       *lease6 = NULL;
834 	struct ipv6_pool      *pool   = NULL;
835 	struct in6_addr        addr;
836 	struct data_string     lease_dhcid;
837 
838 	/*
839 	 * If the lease was static (for a fixed address)
840 	 * we don't need to do any work.
841 	 */
842 	if (ddns_cb->flags & DDNS_STATIC_LEASE)
843 		return (ISC_R_SUCCESS);
844 
845 	/*
846 	 * If we are processing an expired or released v6 lease
847 	 * or some types of v4 leases we don't actually have a
848 	 * scope to update
849 	 */
850 	if ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)
851 		return (ISC_R_SUCCESS);
852 
853 	if (inscope != NULL) {
854 		scope = inscope;
855 	} else if (ddns_cb->address.len == 4) {
856 		if (find_lease_by_ip_addr(&lease, ddns_cb->address, MDL) != 0){
857 			scope = &(lease->scope);
858 		}
859 	} else if (ddns_cb->address.len == 16) {
860 		memcpy(&addr, &ddns_cb->address.iabuf, 16);
861 		if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) ==
862 		     ISC_R_SUCCESS) ||
863 		    (find_ipv6_pool(&pool, D6O_IA_NA, &addr) ==
864 		     ISC_R_SUCCESS)) {
865 			if (iasubopt_hash_lookup(&lease6,  pool->leases,
866 						 &addr, 16, MDL)) {
867 				scope = &(lease6->scope);
868 			}
869 			ipv6_pool_dereference(&pool, MDL);
870 		}
871 	} else {
872 		log_fatal("Impossible condition at %s:%d.", MDL);
873 	}
874 
875 	if (scope == NULL) {
876 		/* If necessary get rid of the lease */
877 		if (lease) {
878 			lease_dereference(&lease, MDL);
879 		}
880 		else if (lease6) {
881 			iasubopt_dereference(&lease6, MDL);
882 		}
883 
884 		return(ISC_R_FAILURE);
885 	}
886 
887 	/* We now have a scope and can proceed to update it */
888 	switch(ddns_cb->state) {
889 	case DDNS_STATE_REM_PTR:
890 		unset(*scope, "ddns-rev-name");
891 		if ((ddns_cb->flags & DDNS_CLIENT_DID_UPDATE) != 0) {
892 			unset(*scope, "ddns-client-fqdn");
893 		}
894 		break;
895 
896 	case DDNS_STATE_ADD_PTR:
897 	case DDNS_STATE_CLEANUP:
898 		bind_ds_value(scope, "ddns-rev-name", &ddns_cb->rev_name);
899 		if ((ddns_cb->flags & DDNS_UPDATE_ADDR) == 0) {
900 			bind_ds_value(scope, "ddns-client-fqdn",
901 				      &ddns_cb->fwd_name);
902 		}
903 		break;
904 
905 	case DDNS_STATE_ADD_FW_YXDHCID:
906 	case DDNS_STATE_ADD_FW_NXDOMAIN:
907 	case DDNS_STATE_DSMM_FW_ADD3:
908 		bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name);
909 
910 		if (ddns_cb->lease_tag == ddns_standard_tag) {
911 			bind_ds_value(scope, ddns_standard_tag,
912 				      &ddns_cb->dhcid);
913 		} else {
914 			/* convert from dns version to lease version of dhcid */
915 			memset(&lease_dhcid, 0, sizeof(lease_dhcid));
916 			dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid);
917 			bind_ds_value(scope, ddns_interim_tag, &lease_dhcid);
918 			data_string_forget(&lease_dhcid, MDL);
919 		}
920 		break;
921 
922 	case DDNS_STATE_REM_FW_NXRR:
923 	case DDNS_STATE_REM_FW_YXDHCID:
924 	case DDNS_STATE_REM_FW_DSMM_OTHER:
925 		unset(*scope, "ddns-fwd-name");
926 		unset(*scope, ddns_cb->lease_tag);
927 		break;
928 	}
929 
930 	/* If necessary write it out and get rid of the lease */
931 	if (lease) {
932 		write_lease(lease);
933 		lease_dereference(&lease, MDL);
934 	} else if (lease6) {
935 		write_ia(lease6->ia);
936 		iasubopt_dereference(&lease6, MDL);
937 	}
938 
939 	return(ISC_R_SUCCESS);
940 }
941 
942 #ifdef notdef
943 /*
944  * This function should be called when update_lease_ptr function fails.
945  * It does inform user about the condition, provides some hints how to
946  * resolve this and dies gracefully. This can happend in at least three
947  * cases (all are configuration mistakes):
948  * a) IPv4: user have duplicate fixed-address entries (the same
949  *    address is defined twice). We may have found wrong lease.
950  * b) IPv6: user have overlapping pools (we tried to find
951  *    a lease in a wrong pool)
952  * c) IPv6: user have duplicate fixed-address6 entires (the same
953  *    address is defined twice). We may have found wrong lease.
954  *
955  * Comment: while it would be possible to recover from both cases
956  * by forcibly searching for leases in *all* following pools, that would
957  * only hide the real problem - a misconfiguration. Proper solution
958  * is to log the problem, die and let the user fix his config file.
959  */
960 void
update_lease_failed(struct lease * lease,struct iasubopt * lease6,dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_cb_t * ddns_cb_set,const char * file,int line)961 update_lease_failed(struct lease *lease,
962 		    struct iasubopt *lease6,
963 		    dhcp_ddns_cb_t  *ddns_cb,
964 		    dhcp_ddns_cb_t  *ddns_cb_set,
965 		    const char * file, int line)
966 {
967 	char lease_address[MAX_ADDRESS_STRING_LEN + 64];
968 	char reason[128]; /* likely reason */
969 
970 	sprintf(reason, "unknown");
971 	sprintf(lease_address, "unknown");
972 
973 	/*
974 	 * let's pretend that everything is ok, so we can continue for
975 	 * information gathering purposes
976 	 */
977 
978 	if (ddns_cb != NULL) {
979 		strncpy(lease_address, piaddr(ddns_cb->address),
980 			MAX_ADDRESS_STRING_LEN);
981 
982 		if (ddns_cb->address.len == 4) {
983 			sprintf(reason, "duplicate IPv4 fixed-address entry");
984 		} else if (ddns_cb->address.len == 16) {
985 			sprintf(reason, "duplicate IPv6 fixed-address6 entry "
986 				"or overlapping pools");
987 		} else {
988 			/*
989 			 * Should not happen. We have non-IPv4, non-IPv6
990 			 * address. Something is very wrong here.
991 			 */
992 			sprintf(reason, "corrupted ddns_cb structure (address "
993 				"length is %d)", ddns_cb->address.len);
994 		}
995 	}
996 
997 	log_error("Failed to properly update internal lease structure with "
998 		  "DDNS");
999 	log_error("control block structures. Tried to update lease for"
1000 		  "%s address, ddns_cb=%p.", lease_address, ddns_cb);
1001 
1002 	log_error("%s", "");
1003 	log_error("This condition can occur, if DHCP server configuration is "
1004 		  "inconsistent.");
1005 	log_error("In particular, please do check that your configuration:");
1006 	log_error("a) does not have overlapping pools (especially containing");
1007 	log_error("   %s address).", lease_address);
1008 	log_error("b) there are no duplicate fixed-address or fixed-address6");
1009 	log_error("entries for the %s address.", lease_address);
1010 	log_error("%s", "");
1011 	log_error("Possible reason for this failure: %s", reason);
1012 
1013 	log_fatal("%s(%d): Failed to update lease database with DDNS info for "
1014 		  "address %s. Lease database inconsistent. Unable to recover."
1015 		  " Terminating.", file, line, lease_address);
1016 }
1017 #endif
1018 
1019 /*
1020  * utility function to update found lease. It does extra checks
1021  * that we are indeed updating the right lease. It may happen
1022  * that user have duplicate fixed-address entries, so we attempt
1023  * to update wrong lease. See also safe_lease6_update.
1024  */
1025 
1026 static void
safe_lease_update(struct lease * lease,dhcp_ddns_cb_t * oldcb,dhcp_ddns_cb_t * newcb,const char * file,int line)1027 safe_lease_update(struct lease *lease,
1028 		  dhcp_ddns_cb_t *oldcb,
1029 		  dhcp_ddns_cb_t *newcb,
1030 		  const char *file, int line)
1031 {
1032 	if (lease == NULL) {
1033 		/* should never get here */
1034 		log_fatal("Impossible condition at %s:%d (called from %s:%d).",
1035 			  MDL, file, line);
1036 	}
1037 
1038 	if ( (lease->ddns_cb == NULL) && (newcb == NULL) ) {
1039 		/*
1040 		 * Trying to clean up pointer that is already null. We
1041 		 * are most likely trying to update wrong lease here.
1042 		 */
1043 
1044 		/*
1045 		 * Previously this error message popped out during
1046 		 * DNS update for fixed leases.  As we no longer
1047 		 * try to update the lease for a fixed (static) lease
1048 		 * this should not be a problem.
1049 		 */
1050 		log_error("%s(%d): Invalid lease update. Tried to "
1051 			  "clear already NULL DDNS control block "
1052 			  "pointer for lease %s.",
1053 			  file, line, piaddr(lease->ip_addr) );
1054 
1055 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1056 		update_lease_failed(lease, NULL, oldcb, newcb, file, line);
1057 #endif
1058 		/*
1059 		 * May not reach this: update_lease_failed calls
1060 		 * log_fatal.
1061 		 */
1062 		return;
1063 	}
1064 
1065 	if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) {
1066 		/*
1067 		 * There is existing cb structure, but it differs from
1068 		 * what we expected to see there. Most likely we are
1069 		 * trying to update wrong lease.
1070 		 */
1071 		log_error("%s(%d): Failed to update internal lease "
1072 			  "structure with DDNS control block. Existing"
1073 			  " ddns_cb structure does not match "
1074 			  "expectations.IPv4=%s, old ddns_cb=%p, tried"
1075 			  "to update to new ddns_cb=%p", file, line,
1076 			  piaddr(lease->ip_addr), oldcb,  newcb);
1077 
1078 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1079 		update_lease_failed(lease, NULL, oldcb, newcb, file, line);
1080 #endif
1081 		/*
1082 		 * May not reach this: update_lease_failed calls
1083 		 * log_fatal.
1084 		 */
1085 		return;
1086 	}
1087 
1088 	/* additional IPv4 specific checks may be added here */
1089 
1090 	/* update the lease */
1091 	lease->ddns_cb = newcb;
1092 }
1093 
1094 static void
safe_lease6_update(struct iasubopt * lease6,dhcp_ddns_cb_t * oldcb,dhcp_ddns_cb_t * newcb,const char * file,int line)1095 safe_lease6_update(struct iasubopt *lease6,
1096 		   dhcp_ddns_cb_t *oldcb,
1097 		   dhcp_ddns_cb_t *newcb,
1098 		   const char *file, int line)
1099 {
1100 	char addrbuf[MAX_ADDRESS_STRING_LEN];
1101 
1102 	if (lease6 == NULL) {
1103 		/* should never get here */
1104 		log_fatal("Impossible condition at %s:%d (called from %s:%d).",
1105 			  MDL, file, line);
1106 	}
1107 
1108 	if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) {
1109 		inet_ntop(AF_INET6, &lease6->addr, addrbuf,
1110 			  MAX_ADDRESS_STRING_LEN);
1111 		/*
1112 		 * Trying to clean up pointer that is already null. We
1113 		 * are most likely trying to update wrong lease here.
1114 		 */
1115 		log_error("%s(%d): Failed to update internal lease "
1116 			  "structure. Tried to clear already NULL "
1117 			  "DDNS control block pointer for lease %s.",
1118 			  file, line, addrbuf);
1119 
1120 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1121 		update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
1122 #endif
1123 
1124 		/*
1125 		 * May not reach this: update_lease_failed calls
1126 		 * log_fatal.
1127 		 */
1128 		return;
1129 	}
1130 
1131 	if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) {
1132 		/*
1133 		 * there is existing cb structure, but it differs from
1134 		 * what we expected to see there. Most likely we are
1135 		 * trying to update wrong lease.
1136 		 */
1137 		inet_ntop(AF_INET6, &lease6->addr, addrbuf,
1138 			  MAX_ADDRESS_STRING_LEN);
1139 
1140 		log_error("%s(%d): Failed to update internal lease "
1141 			  "structure with DDNS control block. Existing"
1142 			  " ddns_cb structure does not match "
1143 			  "expectations.IPv6=%s, old ddns_cb=%p, tried"
1144 			  "to update to new ddns_cb=%p", file, line,
1145 			  addrbuf, oldcb,  newcb);
1146 
1147 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1148 		update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
1149 #endif
1150 		/*
1151 		 * May not reach this: update_lease_failed calls
1152 		 * log_fatal.
1153 		 */
1154 		return;
1155 	}
1156 	/* additional IPv6 specific checks may be added here */
1157 
1158 	/* update the lease */
1159 	lease6->ddns_cb = newcb;
1160 }
1161 
1162 /*
1163  * Utility function to update the pointer to the DDNS control block
1164  * in a lease.
1165  * SUCCESS - able to update the pointer
1166  * FAILURE - lease didn't exist or sanity checks failed
1167  * lease and lease6 may be empty in which case we attempt to find
1168  * the lease from the ddns_cb information.
1169  * ddns_cb is the control block to use if a lookup is necessary
1170  * ddns_cb_set is the pointer to insert into the lease and may be NULL
1171  * The last two arguments may look odd as they will be the same much of the
1172  * time, but I need an argument to tell me if I'm setting or clearing in
1173  * addition to the address information from the cb to look up the lease.
1174  * using the same value twice allows me more flexibility.
1175  */
1176 
1177 static isc_result_t
ddns_update_lease_ptr(struct lease * lease,struct iasubopt * lease6,dhcp_ddns_cb_t * ddns_cb,dhcp_ddns_cb_t * ddns_cb_set,const char * file,int line)1178 ddns_update_lease_ptr(struct lease    *lease,
1179 		      struct iasubopt *lease6,
1180 		      dhcp_ddns_cb_t  *ddns_cb,
1181 		      dhcp_ddns_cb_t  *ddns_cb_set,
1182 		      const char * file, int line)
1183 {
1184 	char ddns_address[MAX_ADDRESS_STRING_LEN];
1185 	sprintf(ddns_address, "unknown");
1186 	if (ddns_cb == NULL) {
1187 		log_info("%s(%d): No control block for lease update",
1188 			 file, line);
1189 		return (ISC_R_FAILURE);
1190 	}
1191 	else {
1192 		strcpy(ddns_address, piaddr(ddns_cb->address));
1193 	}
1194 #if defined (DEBUG_DNS_UPDATES)
1195 	log_info("%s(%d): Updating lease_ptr for ddns_cp=%p (addr=%s)",
1196 		 file, line, ddns_cb, ddns_address );
1197 #endif
1198 
1199 	/*
1200 	 * If the lease was static (for a fixed address)
1201 	 * we don't need to do any work.
1202 	 */
1203 	if (ddns_cb->flags & DDNS_STATIC_LEASE) {
1204 #if defined (DEBUG_DNS_UPDATES)
1205 		log_info("lease is static, returning");
1206 #endif
1207 		return (ISC_R_SUCCESS);
1208 	}
1209 
1210 	/*
1211 	 * If we are processing an expired or released v6 lease
1212 	 * we don't actually have a lease to update
1213 	 */
1214 	if ((ddns_cb->address.len == 16) &&
1215 	    ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)) {
1216 		return (ISC_R_SUCCESS);
1217 	}
1218 
1219 	if (lease != NULL) {
1220 		safe_lease_update(lease, ddns_cb, ddns_cb_set,
1221 				  file, line);
1222 	} else if (lease6 != NULL) {
1223 		safe_lease6_update(lease6, ddns_cb, ddns_cb_set,
1224 				  file, line);
1225 	} else if (ddns_cb->address.len == 4) {
1226 		struct lease *find_lease = NULL;
1227 		if (find_lease_by_ip_addr(&find_lease,
1228 					  ddns_cb->address, MDL) != 0) {
1229 #if defined (DEBUG_DNS_UPDATES)
1230 			log_info("%s(%d): find_lease_by_ip_addr(%s) successful:"
1231 				 "lease=%p", file, line, ddns_address,
1232 				 find_lease);
1233 #endif
1234 
1235 			safe_lease_update(find_lease, ddns_cb,
1236 					  ddns_cb_set, file, line);
1237 			lease_dereference(&find_lease, MDL);
1238 		}
1239 		else {
1240 			log_error("%s(%d): ddns_update_lease_ptr failed. "
1241 				  "Lease for %s not found.",
1242 				  file, line, piaddr(ddns_cb->address));
1243 
1244 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1245 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
1246 					    file, line);
1247 #endif
1248 			/*
1249 			 * may not reach this. update_lease_failed
1250 			 * calls log_fatal.
1251 			 */
1252 			return(ISC_R_FAILURE);
1253 
1254 		}
1255 	} else if (ddns_cb->address.len == 16) {
1256 		struct iasubopt *find_lease6 = NULL;
1257 		struct ipv6_pool *pool = NULL;
1258 		struct in6_addr addr;
1259 		char addrbuf[MAX_ADDRESS_STRING_LEN];
1260 
1261 		memcpy(&addr, &ddns_cb->address.iabuf, 16);
1262 		if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) !=
1263 		     ISC_R_SUCCESS) &&
1264 		    (find_ipv6_pool(&pool, D6O_IA_NA, &addr) !=
1265 		     ISC_R_SUCCESS)) {
1266 			inet_ntop(AF_INET6, &addr, addrbuf,
1267 				  MAX_ADDRESS_STRING_LEN);
1268 			log_error("%s(%d): Pool for lease %s not found.",
1269 				  file, line, addrbuf);
1270 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1271 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
1272 					    file, line);
1273 #endif
1274 			/*
1275 			 * never reached. update_lease_failed
1276 			 * calls log_fatal.
1277 			 */
1278 			return(ISC_R_FAILURE);
1279 		}
1280 
1281 		if (iasubopt_hash_lookup(&find_lease6, pool->leases,
1282 					 &addr, 16, MDL)) {
1283 			find_lease6->ddns_cb = ddns_cb_set;
1284 			iasubopt_dereference(&find_lease6, MDL);
1285 		} else {
1286 			inet_ntop(AF_INET6, &addr, addrbuf,
1287 				  MAX_ADDRESS_STRING_LEN);
1288 			log_error("%s(%d): Lease %s not found within pool.",
1289 				  file, line, addrbuf);
1290 #if defined (DNS_UPDATES_MEMORY_CHECKS)
1291 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
1292 					    file, line);
1293 #endif
1294 			/*
1295 			 * not reached when update_lease_failed is called,
1296 			 * it calls log_fatal.
1297 			 */
1298 			ipv6_pool_dereference(&pool, MDL);
1299 			return(ISC_R_FAILURE);
1300 		}
1301 		ipv6_pool_dereference(&pool, MDL);
1302 	} else {
1303 		/* shouldn't get here */
1304 		log_fatal("Impossible condition at %s:%d, called from %s:%d.",
1305 			  MDL, file, line);
1306 	}
1307 
1308 	return(ISC_R_SUCCESS);
1309 }
1310 
1311 static void
ddns_ptr_add(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1312 ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb,
1313 	     isc_result_t    eresult)
1314 {
1315 	if (eresult == ISC_R_SUCCESS) {
1316 		log_info("Added reverse map from %.*s to %.*s",
1317 			 (int)ddns_cb->rev_name.len,
1318 			 (const char *)ddns_cb->rev_name.data,
1319 			 (int)ddns_cb->fwd_name.len,
1320 			 (const char *)ddns_cb->fwd_name.data);
1321 
1322 		ddns_update_lease_text(ddns_cb, NULL);
1323 	} else {
1324 		log_error("Unable to add reverse map from %.*s to %.*s: %s",
1325 			  (int)ddns_cb->rev_name.len,
1326 			  (const char *)ddns_cb->rev_name.data,
1327 			  (int)ddns_cb->fwd_name.len,
1328 			  (const char *)ddns_cb->fwd_name.data,
1329 			  isc_result_totext (eresult));
1330 	}
1331 
1332 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1333 	destroy_ddns_cb(ddns_cb, MDL);
1334 	/*
1335 	 * A single DDNS operation may require several calls depending on
1336 	 * the current state as the prerequisites for the first message
1337 	 * may not succeed requiring a second operation and potentially
1338 	 * a ptr operation after that.  The commit_leases operation is
1339 	 * invoked at the end of this set of operations in order to require
1340 	 * a single write for all of the changes.  We call commit_leases
1341 	 * here rather than immediately after the call to update the lease
1342 	 * text in order to save any previously written data.
1343 	 */
1344 	commit_leases();
1345 	return;
1346 }
1347 
1348 /*
1349  * action routine when trying to remove a pointer
1350  * this will be called after the ddns queries have completed
1351  * if we succeeded in removing the pointer we go to the next step (if any)
1352  * if not we cleanup and leave.
1353  */
1354 
1355 static void
ddns_ptr_remove(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1356 ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb,
1357 		isc_result_t    eresult)
1358 {
1359 	isc_result_t result = eresult;
1360 
1361 	switch(eresult) {
1362 	case ISC_R_SUCCESS:
1363 		log_info("Removed reverse map on %.*s",
1364 			 (int)ddns_cb->rev_name.len,
1365 			 (const char *)ddns_cb->rev_name.data);
1366 		/* fall through */
1367 	case DNS_R_NXRRSET:
1368 	case DNS_R_NXDOMAIN:
1369 		/* No entry is the same as success.
1370 		 * Remove the information from the lease and
1371 		 * continue with any next step */
1372 		ddns_update_lease_text(ddns_cb, NULL);
1373 
1374 		/* trigger any add operation */
1375 		result = ISC_R_SUCCESS;
1376 #if defined (DEBUG_DNS_UPDATES)
1377 		log_info("DDNS: removed map or no reverse map to remove %.*s",
1378 			 (int)ddns_cb->rev_name.len,
1379 			 (const char *)ddns_cb->rev_name.data);
1380 #endif
1381 		break;
1382 
1383 	default:
1384 		log_error("Can't remove reverse map on %.*s: %s",
1385 			  (int)ddns_cb->rev_name.len,
1386 			  (const char *)ddns_cb->rev_name.data,
1387 			  isc_result_totext (eresult));
1388 		break;
1389 	}
1390 
1391 	/* If we aren't suppossed to do the next step, set the result
1392 	 * flag so ddns_fwd_srv_connector won't do much
1393 	 */
1394 	if ((ddns_cb->flags & DDNS_EXECUTE_NEXT) == 0)
1395 		result = ISC_R_FAILURE;
1396 
1397 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1398 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result);
1399 	destroy_ddns_cb(ddns_cb, MDL);
1400 	return;
1401 }
1402 
1403 
1404 /*
1405  * If the first query succeeds, the updater can conclude that it
1406  * has added a new name whose only RRs are the A and DHCID RR records.
1407  * The A RR update is now complete (and a client updater is finished,
1408  * while a server might proceed to perform a PTR RR update).
1409  *   -- "Interaction between DHCP and DNS"
1410  *
1411  * If the second query succeeds, the updater can conclude that the current
1412  * client was the last client associated with the domain name, and that
1413  * the name now contains the updated A RR. The A RR update is now
1414  * complete (and a client updater is finished, while a server would
1415  * then proceed to perform a PTR RR update).
1416  *   -- "Interaction between DHCP and DNS"
1417  *
1418  * If the second query fails with NXRRSET, the updater must conclude
1419  * that the client's desired name is in use by another host.  If
1420  * Dual Stack Mixed Mode (DSMM) is enabled and we proceed to a
1421  * third stage forward update attempt specific to DSMM rules.  If not,
1422  * then the existing entries are left intact:
1423  *
1424  * At this juncture, the updater can decide (based on some administrative
1425  * configuration outside of the scope of this document) whether to let
1426  * the existing owner of the name keep that name, and to (possibly)
1427  * perform some name disambiguation operation on behalf of the current
1428  * client, or to replace the RRs on the name with RRs that represent
1429  * the current client. If the configured policy allows replacement of
1430  * existing records, the updater submits a query that deletes the
1431  * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
1432  * represent the IP address and client-identity of the new client.
1433  *   -- "Interaction between DHCP and DNS"
1434  */
1435 
1436 static void
ddns_fwd_srv_add2(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1437 ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb,
1438 		  isc_result_t    eresult)
1439 {
1440 	isc_result_t result;
1441 	const char *logstr = NULL;
1442 	char ddns_address[MAX_ADDRESS_STRING_LEN];
1443 
1444 #if defined (DEBUG_DNS_UPDATES)
1445 	log_info ("DDNS:ddns_fwd_srv_add2: %s eresult: %d",
1446 		  dump_ddns_cb(ddns_cb), eresult);
1447 #endif
1448 
1449 	/* Construct a printable form of the address for logging */
1450 	strcpy(ddns_address, piaddr(ddns_cb->address));
1451 
1452 	switch(eresult) {
1453 	case ISC_R_SUCCESS:
1454 		log_info("Added new forward map from %.*s to %s",
1455 			 (int)ddns_cb->fwd_name.len,
1456 			 (const char *)ddns_cb->fwd_name.data,
1457 			 ddns_address);
1458 
1459 		ddns_update_lease_text(ddns_cb, NULL);
1460 
1461 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
1462 			/* if we have zone information get rid of it */
1463 			if (ddns_cb->zone != NULL) {
1464 				ddns_cb_forget_zone(ddns_cb);
1465 			}
1466 
1467 			ddns_cb->state = DDNS_STATE_ADD_PTR;
1468 			ddns_cb->cur_func = ddns_ptr_add;
1469 
1470 			result = ddns_modify_ptr(ddns_cb, MDL);
1471 			if (result == ISC_R_SUCCESS) {
1472 				return;
1473 			}
1474 		}
1475 		break;
1476 
1477 	case DNS_R_YXRRSET:
1478 	case DNS_R_YXDOMAIN:
1479 		logstr = "DHCID mismatch, belongs to another client.";
1480 		break;
1481 
1482 	case DNS_R_NXDOMAIN:
1483 	case DNS_R_NXRRSET:
1484 		/* If DSMM is on we need to try forward add3 */
1485 		if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
1486 			ddns_cb->state = DDNS_STATE_DSMM_FW_ADD3;
1487 			ddns_cb->cur_func = ddns_fwd_srv_add3;
1488 
1489 			result = ddns_modify_fwd(ddns_cb, MDL);
1490 			if (result == ISC_R_SUCCESS) {
1491 				return;
1492 			}
1493 
1494 			break;
1495 		}
1496 
1497 		logstr = "Has an address record but no DHCID, not mine.";
1498 		break;
1499 
1500 	default:
1501 		logstr = isc_result_totext(eresult);
1502 		break;
1503 	}
1504 
1505 	if (logstr != NULL) {
1506 		log_error("Forward map from %.*s to %s FAILED: %s",
1507 			  (int)ddns_cb->fwd_name.len,
1508 			  (const char *)ddns_cb->fwd_name.data,
1509 			  ddns_address, logstr);
1510 	}
1511 
1512 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1513 	destroy_ddns_cb(ddns_cb, MDL);
1514 	/*
1515 	 * A single DDNS operation may require several calls depending on
1516 	 * the current state as the prerequisites for the first message
1517 	 * may not succeed requiring a second operation and potentially
1518 	 * a ptr operation after that.  The commit_leases operation is
1519 	 * invoked at the end of this set of operations in order to require
1520 	 * a single write for all of the changes.  We call commit_leases
1521 	 * here rather than immediately after the call to update the lease
1522 	 * text in order to save any previously written data.
1523 	 */
1524 	commit_leases();
1525 	return;
1526 }
1527 
1528 static void
ddns_fwd_srv_add1(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1529 ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb,
1530 		  isc_result_t    eresult)
1531 {
1532 	isc_result_t result;
1533 	char ddns_address[MAX_ADDRESS_STRING_LEN];
1534 
1535 #if defined (DEBUG_DNS_UPDATES)
1536 	log_info ("DDNS: ddns_fwd_srv_add1: %s eresult: %d",
1537 		  dump_ddns_cb(ddns_cb), eresult);
1538 #endif
1539 
1540 	/* Construct a printable form of the address for logging */
1541 	strcpy(ddns_address, piaddr(ddns_cb->address));
1542 
1543 	switch(eresult) {
1544 	case ISC_R_SUCCESS:
1545 		log_info ("Added new forward map from %.*s to %s",
1546 			  (int)ddns_cb->fwd_name.len,
1547 			  (const char *)ddns_cb->fwd_name.data,
1548 			  ddns_address);
1549 
1550 		ddns_update_lease_text(ddns_cb, NULL);
1551 
1552 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
1553 			/* if we have zone information get rid of it */
1554 			if (ddns_cb->zone != NULL) {
1555 				ddns_cb_forget_zone(ddns_cb);
1556 			}
1557 
1558 			ddns_cb->state = DDNS_STATE_ADD_PTR;
1559 			ddns_cb->cur_func = ddns_ptr_add;
1560 
1561 			result = ddns_modify_ptr(ddns_cb, MDL);
1562 			if (result == ISC_R_SUCCESS) {
1563 				return;
1564 			}
1565 		}
1566 		break;
1567 
1568 	case DNS_R_YXDOMAIN:
1569 		/* we can reuse the zone information */
1570 		ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
1571 		ddns_cb->cur_func = ddns_fwd_srv_add2;
1572 
1573 		result = ddns_modify_fwd(ddns_cb, MDL);
1574 		if (result == ISC_R_SUCCESS) {
1575 			return;
1576 		}
1577 		break;
1578 	default:
1579 		log_error ("Unable to add forward map from %.*s to %s: %s",
1580 			   (int)ddns_cb->fwd_name.len,
1581 			   (const char *)ddns_cb->fwd_name.data,
1582 			   ddns_address,
1583 			   isc_result_totext (eresult));
1584 		break;
1585 	}
1586 
1587 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1588 	destroy_ddns_cb(ddns_cb, MDL);
1589 	/*
1590 	 * A single DDNS operation may require several calls depending on
1591 	 * the current state as the prerequisites for the first message
1592 	 * may not succeed requiring a second operation and potentially
1593 	 * a ptr operation after that.  The commit_leases operation is
1594 	 * invoked at the end of this set of operations in order to require
1595 	 * a single write for all of the changes.  We call commit_leases
1596 	 * here rather than immediately after the call to update the lease
1597 	 * text in order to save any previously written data.
1598 	 */
1599 	commit_leases();
1600 	return;
1601 }
1602 
1603 /*
1604  * This action routine is invoked after the DSMM third add stage is
1605  * attempted.  If we succeeded we attempt to update the reverse DNS,
1606  * if not we cleanup and leave.
1607  */
1608 void
ddns_fwd_srv_add3(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1609 ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb,
1610 		  isc_result_t    eresult)
1611 {
1612 	isc_result_t result;
1613 	const char *logstr = NULL;
1614 	char ddns_address[MAX_ADDRESS_STRING_LEN+1];
1615 
1616 #if defined (DEBUG_DNS_UPDATES)
1617 	log_info ("DDNS: ddns_fwd_srv_add3: %s eresult: %d",
1618 		  dump_ddns_cb(ddns_cb), eresult);
1619 #endif
1620 
1621 	/* Construct a printable form of the address for logging */
1622 	memset(ddns_address, 0x0, sizeof(ddns_address));
1623 	strncpy(ddns_address, piaddr(ddns_cb->address),
1624                 sizeof(ddns_address) - 1);
1625 
1626 	switch(eresult) {
1627 	case ISC_R_SUCCESS:
1628 		log_info("Added new forward map from %.*s to %s",
1629 			 (int)ddns_cb->fwd_name.len,
1630 			 (const char *)ddns_cb->fwd_name.data,
1631 			 ddns_address);
1632 
1633 		ddns_update_lease_text(ddns_cb, NULL);
1634 
1635 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
1636 			/* if we have zone information get rid of it */
1637 			if (ddns_cb->zone != NULL) {
1638 				ddns_cb_forget_zone(ddns_cb);
1639 			}
1640 
1641 			ddns_cb->state = DDNS_STATE_ADD_PTR;
1642 			ddns_cb->cur_func = ddns_ptr_add;
1643 
1644 			result = ddns_modify_ptr(ddns_cb, MDL);
1645 			if (result == ISC_R_SUCCESS) {
1646 				return;
1647 			}
1648 		}
1649 		break;
1650 
1651 	case DNS_R_YXRRSET:
1652 		logstr = "an entry that is either static or "
1653 			 "owned by another client exists.";
1654 		break;
1655 
1656 	case DNS_R_NXRRSET:
1657 		logstr = "static entry of the other protocol type exists.";
1658 		break;
1659 
1660 	default:
1661 		logstr = isc_result_totext(eresult);
1662 		break;
1663 	}
1664 
1665 	if (logstr != NULL) {
1666 		log_error("Forward map from %.*s to %s FAILED: %s",
1667 			  (int)ddns_cb->fwd_name.len,
1668 			  (const char *)ddns_cb->fwd_name.data,
1669 			  ddns_address, logstr);
1670 	}
1671 
1672 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1673 	destroy_ddns_cb(ddns_cb, MDL);
1674 	/*
1675 	 * A single DDNS operation may require several calls depending on
1676 	 * the current state as the prerequisites for the first message
1677 	 * may not succeed requiring a second operation and potentially
1678 	 * a ptr operation after that.  The commit_leases operation is
1679 	 * invoked at the end of this set of operations in order to require
1680 	 * a single write for all of the changes.  We call commit_leases
1681 	 * here rather than immediately after the call to update the lease
1682 	 * text in order to save any previously written data.
1683 	 */
1684 	commit_leases();
1685 	return;
1686 }
1687 
1688 static void
ddns_fwd_srv_connector(struct lease * lease,struct iasubopt * lease6,struct binding_scope ** inscope,dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1689 ddns_fwd_srv_connector(struct lease          *lease,
1690 		       struct iasubopt       *lease6,
1691 		       struct binding_scope **inscope,
1692 		       dhcp_ddns_cb_t        *ddns_cb,
1693 		       isc_result_t           eresult)
1694 {
1695 	isc_result_t result = ISC_R_FAILURE;
1696 
1697 #if defined (DEBUG_DNS_UPDATES)
1698 	log_info ("DDNS: ddns_fwd_srv_connector: %s eresult: %d",
1699 		  dump_ddns_cb(ddns_cb), eresult);
1700 #endif
1701 
1702 	if (ddns_cb == NULL) {
1703 		/* nothing to do */
1704 		return;
1705 	}
1706 
1707 	if (eresult == ISC_R_SUCCESS) {
1708 		/*
1709 		 * If we have updates dispatch as appropriate,
1710 		 * if not do FQDN binding if desired.
1711 		 */
1712 
1713 		if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
1714 			ddns_cb->state    = DDNS_STATE_ADD_FW_NXDOMAIN;
1715 			ddns_cb->cur_func = ddns_fwd_srv_add1;
1716 			result = ddns_modify_fwd(ddns_cb, MDL);
1717 		} else if ((ddns_cb->flags & DDNS_UPDATE_PTR) &&
1718 			 (ddns_cb->rev_name.len != 0)) {
1719 			ddns_cb->state    = DDNS_STATE_ADD_PTR;
1720 			ddns_cb->cur_func = ddns_ptr_add;
1721 			result = ddns_modify_ptr(ddns_cb, MDL);
1722 		} else {
1723 			ddns_update_lease_text(ddns_cb, inscope);
1724 		}
1725 	}
1726 
1727 
1728 	if (result == ISC_R_SUCCESS) {
1729 		ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL);
1730 	} else {
1731 		destroy_ddns_cb(ddns_cb, MDL);
1732 	}
1733 
1734 	return;
1735 }
1736 
1737 /*
1738  * If the first query fails, the updater MUST NOT delete the DNS name.  It
1739  * may be that the host whose lease on the server has expired has moved
1740  * to another network and obtained a lease from a different server,
1741  * which has caused the client's A RR to be replaced. It may also be
1742  * that some other client has been configured with a name that matches
1743  * the name of the DHCP client, and the policy was that the last client
1744  * to specify the name would get the name.  In this case, the DHCID RR
1745  * will no longer match the updater's notion of the client-identity of
1746  * the host pointed to by the DNS name.
1747  *   -- "Interaction between DHCP and DNS"
1748  */
1749 
1750 static void
ddns_fwd_srv_rem2(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1751 ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb,
1752 		  isc_result_t    eresult)
1753 {
1754 #if defined (DEBUG_DNS_UPDATES)
1755 	log_info ("DDNS: ddns_fwd_srv_rem2: %s eresult: %d",
1756 		  dump_ddns_cb(ddns_cb), eresult);
1757 #endif
1758 
1759 	/*
1760 	 * To get here we have already managed to remove the A/AAAA
1761 	 * record and are trying to remove the DHCID/TXT record as well.
1762 	 * On success (removed DHCID/TXT) or YXRRSET (DHCID/TXT still in
1763 	 * use by something else) we clean up the lease.
1764 	 * On some other error we don't clean up the lease and hope that
1765 	 * if we try this again it will work.  An example would be if we
1766 	 * got a timeout as the DNS server halted between the first and
1767 	 * second steps.  The DNS server would still have the DHCID/TXT
1768 	 * and we would like to remove that in the future.
1769 	 *
1770 	 * On success set the EXECUTE_NEXT flag which triggers any
1771 	 * add that is next in the chain.
1772 	 */
1773 	if ((eresult == ISC_R_SUCCESS) ||
1774 	    (eresult == DNS_R_YXRRSET))  {
1775 		ddns_update_lease_text(ddns_cb, NULL);
1776 		eresult = ISC_R_SUCCESS;
1777 	}
1778 
1779 	/* Do the next operation */
1780 	if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
1781 		/* if we have zone information get rid of it */
1782 		if (ddns_cb->zone != NULL) {
1783 			ddns_cb_forget_zone(ddns_cb);
1784 		}
1785 
1786 		ddns_cb->state = DDNS_STATE_REM_PTR;
1787 		ddns_cb->cur_func = ddns_ptr_remove;
1788 		if (eresult == ISC_R_SUCCESS)
1789 			ddns_cb->flags |= DDNS_EXECUTE_NEXT;
1790 
1791 		eresult = ddns_modify_ptr(ddns_cb, MDL);
1792 		if (eresult == ISC_R_SUCCESS) {
1793 			return;
1794 		}
1795 	}
1796 
1797 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1798 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
1799 	destroy_ddns_cb(ddns_cb, MDL);
1800 	return;
1801 }
1802 
1803 
1804 /*
1805  * First action routine when trying to remove a fwd
1806  * this will be called after the ddns queries have completed
1807  * if we succeeded in removing the fwd we go to the next step (if any)
1808  * if not we cleanup and leave.
1809  */
1810 
1811 static void
ddns_fwd_srv_rem1(dhcp_ddns_cb_t * ddns_cb,isc_result_t eresult)1812 ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb,
1813 		  isc_result_t    eresult)
1814 {
1815 	isc_result_t result = eresult;
1816 	char ddns_address[MAX_ADDRESS_STRING_LEN];
1817 
1818 #if defined (DEBUG_DNS_UPDATES)
1819 	log_info ("DDNS: ddns_fwd_srv_rem1: %s eresult: %d",
1820 		  dump_ddns_cb(ddns_cb), eresult);
1821 #endif
1822 
1823 	switch(eresult) {
1824 	case ISC_R_SUCCESS:
1825 		/* Construct a printable form of the address for logging */
1826 		strcpy(ddns_address, piaddr(ddns_cb->address));
1827 		log_info("Removed forward map from %.*s to %s",
1828 			 (int)ddns_cb->fwd_name.len,
1829 			 (const char*)ddns_cb->fwd_name.data,
1830 			 ddns_address);
1831 
1832 		/* Do the second step of the FWD removal */
1833 		ddns_cb->state    = DDNS_STATE_REM_FW_NXRR;
1834 		ddns_cb->cur_func = ddns_fwd_srv_rem2;
1835 		result = ddns_modify_fwd(ddns_cb, MDL);
1836 		if (result == ISC_R_SUCCESS) {
1837 			return;
1838 		}
1839 		break;
1840 
1841 	case DNS_R_NXRRSET:
1842 	case DNS_R_NXDOMAIN:
1843 		/* A result of not found means rem1 did not find a guard of
1844 		 * our * type. From this we assume either there are no address
1845 		 * record(s) of our type to delete or they are unguarded and
1846 		 * therefore presumed to be static. Either way, we're done
1847 		 * unless we're in DSMM and ddns-other-guard-is-dynamic is on.
1848 		 * In which case we need to see if a guard of the other type
1849 		 * exists, which permits us to delete any address records of
1850 		 * our type. */
1851 		#define DSMM_OGD (DDNS_DUAL_STACK_MIXED_MODE | \
1852 				  DDNS_OTHER_GUARD_IS_DYNAMIC)
1853 		if ((ddns_cb->flags & DSMM_OGD) == DSMM_OGD) {
1854 			ddns_cb->state    = DDNS_STATE_REM_FW_DSMM_OTHER;
1855 			ddns_cb->cur_func = ddns_fwd_srv_rem2;
1856 			result = ddns_modify_fwd(ddns_cb, MDL);
1857 			if (result == ISC_R_SUCCESS) {
1858 				return;
1859 			}
1860 			break;
1861 		}
1862 
1863 		ddns_update_lease_text(ddns_cb, NULL);
1864 
1865 #if defined (DEBUG_DNS_UPDATES)
1866 		log_info("DDNS: no forward map to remove. %p", ddns_cb);
1867 #endif
1868 		/* Trigger the add operation */
1869 		eresult = ISC_R_SUCCESS;
1870 
1871 		/* Fall through */
1872 	default:
1873 
1874 		/* We do the remove operation in most cases
1875 		 * but we don't want to continue with adding a forward
1876 		 * record if the forward removal had issues so we
1877 		 * check the eresult and set the EXECUTE_NEXT flag on
1878 		 * success.
1879 		 */
1880 
1881 		/* Do the remove operation */
1882 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
1883 			/* if we have zone information get rid of it */
1884 			if (ddns_cb->zone != NULL) {
1885 				ddns_cb_forget_zone(ddns_cb);
1886 			}
1887 
1888 			ddns_cb->state    = DDNS_STATE_REM_PTR;
1889 			ddns_cb->cur_func = ddns_ptr_remove;
1890 			if (eresult == ISC_R_SUCCESS)
1891 				ddns_cb->flags |= DDNS_EXECUTE_NEXT;
1892 
1893 			result = ddns_modify_ptr(ddns_cb, MDL);
1894 			if (result == ISC_R_SUCCESS) {
1895 				return;
1896 			}
1897 		}
1898 		break;
1899 	}
1900 
1901 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
1902 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
1903 	destroy_ddns_cb(ddns_cb, MDL);
1904 }
1905 
1906 /*%<
1907  * Remove relevant entries from DNS.
1908  *
1909  * \li lease  - lease to start with if this is for v4
1910  *
1911  * \li lease6 - lease to start with if this is for v6
1912  *
1913  * \li add_ddns_cb - control block for additional DDNS work.  This
1914  *     is used when the code is going to add a DDNS entry after removing
1915  *     the current entry.
1916  *
1917  * \li active - indication about the status of the lease. It is
1918  *     ISC_TRUE if the lease is still active, and FALSE if the lease
1919  *     is inactive.  This is used to indicate if the lease is inactive or going
1920  *     to inactive so we can avoid trying to update the lease with cb pointers
1921  *     and text information if it isn't useful.
1922  *
1923  * Returns
1924  * \li #ISC_R_FAILURE - badness occurred and we weren't able to do what was wanted
1925  * \li #ISC_R_SUCCESS - we were able to do stuff but it's in progress
1926  *
1927  * in both cases any additional block has been passed on to it's handler
1928  */
1929 
1930 isc_result_t
ddns_removals(struct lease * lease,struct iasubopt * lease6,dhcp_ddns_cb_t * add_ddns_cb,isc_boolean_t active)1931 ddns_removals(struct lease    *lease,
1932 	      struct iasubopt *lease6,
1933 	      dhcp_ddns_cb_t  *add_ddns_cb,
1934 	      isc_boolean_t    active)
1935 {
1936 	isc_result_t rcode, execute_add = ISC_R_FAILURE;
1937 	struct binding_scope **scope = NULL;
1938 	isc_result_t result = ISC_R_FAILURE;
1939 	dhcp_ddns_cb_t        *ddns_cb = NULL;
1940 	struct data_string     leaseid;
1941 
1942 #if defined (DEBUG_DNS_UPDATES)
1943 	log_info ("DDNS: ddns_removals: %s",
1944 		  dump_ddns_cb(add_ddns_cb));
1945 #endif
1946 
1947 	/*
1948 	 * See if we need to cancel an outstanding request.  Mostly this is
1949 	 * used to handle the case where this routine is called twice for
1950 	 * the same release or abandon event.
1951 	 *
1952 	 * When called from the dns code as part of an update request
1953 	 * (add_ddns_cb != NULL) any outstanding requests will have already
1954 	 * been cancelled.
1955 	 *
1956 	 * If the new request is just a removal and we have an outstanding
1957 	 * request we have several options:
1958 	 *
1959 	 * - we are doing an update or we are doing a removal and the active
1960 	 * flag has changed from TRUE to FALSE.  In these cases we  need to
1961 	 * cancel the old request and start the new one.
1962 	 *
1963 	 * - other wise we are doing a removal with the active flag unchanged.
1964 	 * In this case we can let the current removal continue and do not need
1965 	 * to start a new one.  If the old request included an update to be
1966 	 * done after the removal we need to kill the update part of the
1967 	 * request.
1968 	 */
1969 
1970 	if (add_ddns_cb == NULL) {
1971 		if ((lease != NULL) && (lease->ddns_cb != NULL)) {
1972 			ddns_cb = lease->ddns_cb;
1973 
1974 			/*
1975 			 * Is the old request an update or did the
1976 			 * the active flag change?
1977 			 */
1978 			if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
1979 			     (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
1980 			     (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
1981 			    ((active == ISC_FALSE) &&
1982 			     ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
1983 				/* Cancel the current request */
1984 				ddns_cancel(lease->ddns_cb, MDL);
1985 				lease->ddns_cb = NULL;
1986 			} else {
1987 				/* Remvoval, check and remove updates */
1988 				if (ddns_cb->next_op != NULL) {
1989 					destroy_ddns_cb(ddns_cb->next_op, MDL);
1990 					ddns_cb->next_op = NULL;
1991 				}
1992 #if defined (DEBUG_DNS_UPDATES)
1993 				log_info("DDNS %s(%d): removal already in "
1994 					 "progress new ddns_cb=%p",
1995 					 MDL, ddns_cb);
1996 #endif
1997 				return (ISC_R_SUCCESS);
1998 			}
1999 		} else if ((lease6 != NULL) && (lease6->ddns_cb != NULL)) {
2000 			ddns_cb = lease6->ddns_cb;
2001 
2002 			/*
2003 			 * Is the old request an update or did the
2004 			 * the active flag change?
2005 			 */
2006 			if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
2007 			     (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
2008 			     (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
2009 			    ((active == ISC_FALSE) &&
2010 			     ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
2011 				/* Cancel the current request */
2012 				ddns_cancel(lease6->ddns_cb, MDL);
2013 				lease6->ddns_cb = NULL;
2014 			} else {
2015 				/* Remvoval, check and remove updates */
2016 				if (ddns_cb->next_op != NULL) {
2017 					destroy_ddns_cb(ddns_cb->next_op, MDL);
2018 					ddns_cb->next_op = NULL;
2019 				}
2020 #if defined (DEBUG_DNS_UPDATES)
2021 				log_info("DDNS %s(%d): removal already in "
2022 					 "progress new ddns_cb=%p",
2023 					 MDL, ddns_cb);
2024 #endif
2025 				return (ISC_R_SUCCESS);
2026 			}
2027 		}
2028 		ddns_cb = NULL;
2029 	}
2030 
2031 	/* allocate our control block */
2032 	ddns_cb = ddns_cb_alloc(MDL);
2033 	if (ddns_cb == NULL) {
2034 		goto cleanup;
2035 	}
2036 
2037 	/* Set the conflict detection flags based on global configuration */
2038 	copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
2039 
2040 	/*
2041 	 * For v4 we flag static leases so we don't try
2042 	 * and manipulate the lease later.  For v6 we don't
2043 	 * get static leases and don't need to flag them.
2044 	 */
2045 	if (lease != NULL) {
2046 		scope = &(lease->scope);
2047 		ddns_cb->address = lease->ip_addr;
2048 		if (lease->flags & STATIC_LEASE)
2049 			ddns_cb->flags |= DDNS_STATIC_LEASE;
2050 	} else if (lease6 != NULL) {
2051 		scope = &(lease6->scope);
2052 		memcpy(&ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
2053 		ddns_cb->address.len = 16;
2054 	} else
2055 		goto cleanup;
2056 
2057 	/*
2058 	 * Set the flag bit if the lease is active, that is it isn't
2059 	 * expired or released.  This is used to determine if we need
2060 	 * to update the scope information for both v4 and v6 and
2061 	 * the lease information for v6 when the response
2062 	 * from the DNS code is processed.
2063 	 */
2064 	if (active == ISC_TRUE) {
2065 		ddns_cb->flags |= DDNS_ACTIVE_LEASE;
2066 	}
2067 
2068 	/* No scope implies that DDNS has not been performed for this lease. */
2069 	if (*scope == NULL)
2070 		goto cleanup;
2071 
2072 	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
2073 	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
2074 		goto cleanup;
2075 
2076 	/* Assume that we are removing both records */
2077 	ddns_cb->flags |= DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR;
2078 
2079 	/* and that we want to do the add call */
2080 	execute_add = ISC_R_SUCCESS;
2081 
2082 	/*
2083 	 * Look up stored names.
2084 	 */
2085 
2086 	/*
2087 	 * Find the fwd name and copy it to the control block.  If we don't
2088 	 * have it we can't delete the fwd record but we can still try to
2089 	 * remove the ptr record and cleanup the lease information if the
2090 	 * client did the fwd update.
2091 	 */
2092 	if (!find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-fwd-name")) {
2093 		/* don't try and delete the A, or do the add */
2094 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
2095 		execute_add = ISC_R_FAILURE;
2096 
2097 		/* Check if client did update */
2098 		if (find_bound_string(&ddns_cb->fwd_name, *scope,
2099 				      "ddns-client-fqdn")) {
2100 			ddns_cb->flags |= DDNS_CLIENT_DID_UPDATE;
2101 		}
2102 	}
2103 
2104 	/*
2105 	 * Find the txt or dhcid tag and copy it to the control block. If we
2106 	 * don't have one this isn't an interim or standard record so we can't
2107 	 * delete the A record using this mechanism but we can delete the ptr
2108 	 * record. In this case we will attempt to do any requested next step.
2109 	 */
2110 	memset(&leaseid, 0, sizeof(leaseid));
2111 	if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) {
2112 		/* We have a standard tag */
2113 		ddns_cb->lease_tag = ddns_standard_tag;
2114 		ddns_cb->dhcid_class = dns_rdatatype_dhcid;
2115 		ddns_cb->other_dhcid_class = dns_rdatatype_txt;
2116 		data_string_copy(&ddns_cb->dhcid, &leaseid, MDL);
2117 		data_string_forget(&leaseid, MDL);
2118 	} else 	if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) {
2119 		/* we have an interim tag */
2120 		ddns_cb->lease_tag = ddns_interim_tag;
2121 		ddns_cb->dhcid_class = dns_rdatatype_txt;
2122 		ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
2123 		if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) !=
2124 		    ISC_R_SUCCESS) {
2125 			/* We couldn't convert the dhcid from the lease
2126 			 * version to the dns version.  We can't delete
2127 			 * the A record but can continue to the ptr
2128 			 */
2129 			ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
2130 		}
2131 		data_string_forget(&leaseid, MDL);
2132 	} else {
2133 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
2134 	}
2135 
2136 	/*
2137 	 * Find the rev name and copy it to the control block.  If we don't
2138 	 * have it we can't get rid of it but we can try to remove the fwd
2139 	 * pointer if desired.
2140 	 */
2141 	if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) {
2142 		ddns_cb->flags &= ~DDNS_UPDATE_PTR;
2143 	}
2144 
2145 
2146 	/*
2147 	 * If we have a second control block for doing an add
2148 	 * after the remove finished attach it to our control block.
2149 	 */
2150 	ddns_cb->next_op = add_ddns_cb;
2151 
2152 	/*
2153 	 * Now that we've collected the information we can try to process it.
2154 	 * If necessary we call an appropriate routine to send a message and
2155 	 * provide it with an action routine to run on the control block given
2156 	 * the results of the message.  We have three entry points from here,
2157 	 * one for removing the A record, the next for removing the PTR and
2158 	 * the third for doing any requested add.
2159 	 */
2160 	if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
2161 		if (ddns_cb->fwd_name.len != 0) {
2162 			ddns_cb->state    = DDNS_STATE_REM_FW_YXDHCID;
2163 			ddns_cb->cur_func = ddns_fwd_srv_rem1;
2164 
2165 			rcode = ddns_modify_fwd(ddns_cb, MDL);
2166 			if (rcode == ISC_R_SUCCESS) {
2167 				ddns_update_lease_ptr(lease, lease6, ddns_cb,
2168 						      ddns_cb, MDL);
2169 				return (ISC_R_SUCCESS);
2170 			}
2171 
2172 			/*
2173 			 * We weren't able to process the request tag the
2174 			 * add so we won't execute it.
2175 			 */
2176 			execute_add = ISC_R_FAILURE;
2177 			goto cleanup;
2178 		}
2179 		else {
2180 			/*remove info from scope */
2181 			unset(*scope, "ddns-fwd-name");
2182 			unset(*scope, ddns_cb->lease_tag);
2183 		}
2184 	}
2185 
2186 	if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
2187 		ddns_cb->state      = DDNS_STATE_REM_PTR;
2188 		ddns_cb->cur_func   = ddns_ptr_remove;
2189 		ddns_cb->flags      |= DDNS_EXECUTE_NEXT;
2190 
2191 		/*
2192 		 * if execute add isn't success remove the control block so
2193 		 * it won't be processed when the remove completes.  We
2194 		 * also arrange to clean it up and get rid of it.
2195 		 */
2196 		if (execute_add != ISC_R_SUCCESS) {
2197 		   	ddns_cb->next_op = NULL;
2198 			ddns_fwd_srv_connector(lease, lease6, scope,
2199 					       add_ddns_cb, execute_add);
2200 			add_ddns_cb = NULL;
2201 		}
2202 		else {
2203 			result = ISC_R_SUCCESS;
2204 		}
2205 
2206 		rcode = ddns_modify_ptr(ddns_cb, MDL);
2207 		if (rcode == ISC_R_SUCCESS) {
2208 			ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb,
2209 					      MDL);
2210 			return (result);
2211 		}
2212 
2213 		/* We weren't able to process the request tag the
2214 		 * add so we won't execute it */
2215 		execute_add = ISC_R_FAILURE;
2216 		goto cleanup;
2217 	}
2218 
2219  cleanup:
2220 	/*
2221 	 * We've gotten here because we didn't need to send a message or
2222 	 * we failed when trying to do so.  We send the additional cb
2223 	 * off to handle sending and/or cleanup and cleanup anything
2224 	 * we allocated here.
2225 	 */
2226 	ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add);
2227 	if (ddns_cb != NULL)
2228 		destroy_ddns_cb(ddns_cb, MDL);
2229 
2230 	return (result);
2231 }
2232 
2233 /* Convenience function for setting flag bits in a mask */
2234 static void
set_flag(u_int16_t * flags,u_int16_t flag,u_int16_t value)2235 set_flag (u_int16_t *flags,
2236 	       u_int16_t flag,
2237                u_int16_t value) {
2238 	if (flags) {
2239 		if (value) {
2240 			*flags |= flag;
2241 		} else {
2242 			*flags &= ~flag;
2243 		}
2244 	}
2245 }
2246 
2247 /*
2248  * Convenience function which replicates the conflict flags set in one
2249  * mask to another, while preserving all other flags.
2250  */
copy_conflict_flags(u_int16_t * target,u_int16_t source)2251 void copy_conflict_flags(u_int16_t *target,
2252 			 u_int16_t source)  {
2253 	if (target) {
2254 		/* Preserve non conflict flags */
2255 		*target &= ~CONFLICT_BITS;
2256 
2257 		/* Enable conflict flags per source */
2258 		*target |= source & CONFLICT_BITS;
2259 	}
2260 }
2261 
2262 /*
2263  * Given an option_state, create a mask of conflict detection flags based
2264  * on the appropriate configuration parameters within the option state.
2265  */
2266 u_int16_t
get_conflict_mask(struct option_state * options)2267 get_conflict_mask(struct option_state *options) {
2268 
2269 	int ddns_update_conflict_detection = 1;  /* default on  */
2270         int ddns_dual_stack_mixed_mode = 0;	 /* default off */
2271         int ddns_guard_id_must_match = 1;	 /* default on  */
2272         int ddns_other_guard_is_dynamic = 0;	 /* default off */
2273 	struct option_cache *oc = NULL;
2274 
2275 	u_int16_t mask = 0;
2276 	oc = lookup_option(&server_universe, options, SV_DDNS_CONFLICT_DETECT);
2277 	if (oc) {
2278 		ddns_update_conflict_detection =
2279 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
2280 					      NULL, &global_scope, oc, MDL);
2281 	}
2282 
2283 	set_flag(&mask, DDNS_CONFLICT_DETECTION,
2284 		 ddns_update_conflict_detection);
2285 
2286 	if (!ddns_update_conflict_detection) {
2287 #if defined (DEBUG_DNS_UPDATES)
2288 		log_info ("DDNS conflict detection: off");
2289 #endif
2290 		/* Turn the rest of the conflict related flags off */
2291 		set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, 0);
2292 		set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, 0);
2293 		set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, 0);
2294 		return (mask);
2295 	}
2296 
2297 	// Get the values
2298         oc = lookup_option(&server_universe, options,
2299                            SV_DDNS_DUAL_STACK_MIXED_MODE);
2300         if (oc) {
2301                 ddns_dual_stack_mixed_mode =
2302 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
2303 					      NULL, &global_scope, oc, MDL);
2304         }
2305 
2306         oc = lookup_option(&server_universe, options,
2307                            SV_DDNS_GUARD_ID_MUST_MATCH);
2308         if (oc) {
2309                 ddns_guard_id_must_match =
2310 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
2311 					      NULL, &global_scope, oc, MDL);
2312         }
2313 
2314         oc = lookup_option(&server_universe, options,
2315                            SV_DDNS_OTHER_GUARD_IS_DYNAMIC);
2316         if (oc) {
2317                 ddns_other_guard_is_dynamic =
2318 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
2319 					      NULL, &global_scope, oc, MDL);
2320         }
2321 
2322 	// Set the flags
2323 	set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE,
2324 		 ddns_dual_stack_mixed_mode);
2325 
2326 	set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH,
2327 		 ddns_guard_id_must_match);
2328 
2329 	set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC,
2330 		 ddns_other_guard_is_dynamic);
2331 
2332 #if defined (DEBUG_DNS_UPDATES)
2333 	log_info ("DDNS conflict behavior:\n"
2334 		  "\tddns-update-style: %s\n"
2335 		  "\tupdate-conflict-detection: %d\n"
2336 		  "\tddns-dual-stack-mixed-mode: %d\n"
2337 		  "\tddns-guard-id-must-match %d\n"
2338 		  "\tddns-other-guard-is-dynamic: %d\n",
2339 		  ddns_styles_values[ddns_update_style].name,
2340 		  ddns_update_conflict_detection,
2341 		  ddns_dual_stack_mixed_mode,
2342 		  ddns_guard_id_must_match,
2343 		  ddns_other_guard_is_dynamic);
2344 #endif
2345 	return (mask);
2346 }
2347 
2348 #if defined (DEBUG_DNS_UPDATES)
2349 /* Type used for creating lists of function pointers and their names */
2350 typedef struct {
2351 	void *ptr;
2352 	char *name;
2353 } LabeledPtr;
2354 
2355 /* Returns the name of the function referred to by the given address */
2356 char*
dump_ddns_cb_func(void * func)2357 dump_ddns_cb_func(void *func) {
2358 	static LabeledPtr funcs[] = {
2359 		{ ddns_ptr_add, "ddns_ptr_add" },
2360 		{ ddns_fwd_srv_add2, "ddns_fwd_srv_add2" },
2361 		{ ddns_fwd_srv_add1, "ddns_fwd_srv_add1" },
2362 		{ ddns_ptr_remove, "ddns_ptr_remove" },
2363 		{ ddns_fwd_srv_rem2, "ddns_fwd_srv_rem2" },
2364 		{ ddns_fwd_srv_rem1, "ddns_fwd_srv_rem1" },
2365 		{ ddns_fwd_srv_add3, "ddns_fwd_srv_adde" },
2366 		{ NULL, "unknown" }
2367 	};
2368 
2369 	LabeledPtr* lp = funcs;
2370 	if (!func) {
2371 		return ("<null>");
2372 	}
2373 
2374 	while ((lp->ptr) && (lp->ptr != func)) {
2375 		++lp;
2376 	}
2377 
2378 	return (lp->name);
2379 }
2380 
2381 /* Dumps basic control block info to the log */
2382 char*
dump_ddns_cb(dhcp_ddns_cb_t * ddns_cb)2383 dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb) {
2384 	static char output_buf[4096];
2385 	if (!ddns_cb) {
2386 		return ("<ddns_cb is null>");
2387 	}
2388 
2389 	sprintf (output_buf, "ddns_cb: %p flags: %x state: %s cur_func: %s",
2390 		ddns_cb, ddns_cb->flags,
2391 		ddns_state_name(ddns_cb->state),
2392 		dump_ddns_cb_func(ddns_cb->cur_func));
2393 
2394 	return(output_buf);
2395 }
2396 #endif /* DEBUG_DNS_UPDATES */
2397 
2398 #endif /* NSUPDATE */
2399