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