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