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