1/* ddns.c
2
3   Dynamic DNS updates. */
4
5/*
6 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2000-2003 by Internet Software Consortium
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 *   Internet Systems Consortium, Inc.
22 *   950 Charter Street
23 *   Redwood City, CA 94063
24 *   <info@isc.org>
25 *   http://www.isc.org/
26 *
27 * This software has been donated to Internet Systems Consortium
28 * by Damien Neil of Nominum, Inc.
29 *
30 * To learn more about Internet Systems Consortium, see
31 * ``http://www.isc.org/''.   To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
33 */
34
35#ifndef lint
36static char copyright[] =
37"$Id: ddns.c,v 1.8 2011/08/16 16:36:38 christos Exp $ Copyright (c) 2004-2005 Internet Systems Consortium.  All rights reserved.\n";
38#endif /* not lint */
39
40#include "dhcpd.h"
41#include "dst/md5.h"
42#include "minires/minires.h"
43
44#ifdef NSUPDATE
45
46/* DN: No way of checking that there is enough space in a data_string's
47   buffer.  Be certain to allocate enough!
48   TL: This is why the expression evaluation code allocates a *new*
49   data_string.   :') */
50static void data_string_append (struct data_string *ds1,
51				struct data_string *ds2)
52{
53	memcpy (ds1 -> buffer -> data + ds1 -> len,
54		ds2 -> data,
55		ds2 -> len);
56	ds1 -> len += ds2 -> len;
57}
58
59static isc_result_t ddns_update_ptr (struct data_string *ddns_fwd_name,
60				     struct data_string *ddns_rev_name,
61				     unsigned long ttl)
62{
63	ns_updque updqueue;
64	ns_updrec *updrec;
65	isc_result_t result = ISC_R_UNEXPECTED;
66
67	/*
68	 * The DHCP server submits a DNS query which deletes all of the PTR RRs
69	 * associated with the lease IP address, and adds a PTR RR whose data
70	 * is the client's (possibly disambiguated) host name. The server also
71	 * adds a DHCID RR specified in Section 4.3.
72	 *   -- "Interaction between DHCP and DNS"
73	 */
74
75	ISC_LIST_INIT (updqueue);
76
77	/*
78	 * Delete all PTR RRs.
79	 */
80	updrec = minires_mkupdrec (S_UPDATE,
81				   (const char *)ddns_rev_name -> data,
82				   C_IN, T_PTR, 0);
83	if (!updrec) {
84		result = ISC_R_NOMEMORY;
85		goto error;
86	}
87
88	updrec -> r_data = (unsigned char *)0;
89	updrec -> r_size = 0;
90	updrec -> r_opcode = DELETE;
91
92	ISC_LIST_APPEND (updqueue, updrec, r_link);
93
94	/*
95	 * Add PTR RR.
96	 */
97	updrec = minires_mkupdrec (S_UPDATE,
98				   (const char *)ddns_rev_name -> data,
99				   C_IN, T_PTR, ttl);
100	if (!updrec) {
101		result = ISC_R_NOMEMORY;
102		goto error;
103	}
104
105	updrec -> r_data = ddns_fwd_name -> data;
106	updrec -> r_size = ddns_fwd_name -> len;
107	updrec -> r_opcode = ADD;
108
109	ISC_LIST_APPEND (updqueue, updrec, r_link);
110
111	/*
112	 * Attempt to perform the update.
113	 */
114	result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
115#if defined (DEBUG)
116	print_dns_status ((int)result, &updqueue);
117#endif
118	if (result == ISC_R_SUCCESS) {
119		log_info ("added reverse map from %.*s to %.*s",
120			  (int)ddns_rev_name -> len,
121			  (const char *)ddns_rev_name -> data,
122			  (int)ddns_fwd_name -> len,
123			  (const char *)ddns_fwd_name -> data);
124	} else {
125		log_error ("unable to add reverse map from %.*s to %.*s: %s",
126			   (int)ddns_rev_name -> len,
127			   (const char *)ddns_rev_name -> data,
128			   (int)ddns_fwd_name -> len,
129			   (const char *)ddns_fwd_name -> data,
130			   isc_result_totext (result));
131	}
132
133	/* Fall through. */
134      error:
135
136	while (!ISC_LIST_EMPTY (updqueue)) {
137		updrec = ISC_LIST_HEAD (updqueue);
138		ISC_LIST_UNLINK (updqueue, updrec, r_link);
139		minires_freeupdrec (updrec);
140	}
141
142	return result;
143}
144
145
146static isc_result_t ddns_remove_ptr (struct data_string *ddns_rev_name)
147{
148	ns_updque updqueue;
149	ns_updrec *updrec;
150	isc_result_t result;
151
152	/*
153	 * When a lease expires or a DHCP client issues a DHCPRELEASE request,
154	 * the DHCP server SHOULD delete the PTR RR that matches the DHCP
155	 * binding, if one was successfully added. The server's update query
156	 * SHOULD assert that the name in the PTR record matches the name of
157	 * the client whose lease has expired or been released.
158	 *   -- "Interaction between DHCP and DNS"
159	 */
160
161	ISC_LIST_INIT (updqueue);
162
163	/*
164	 * Delete the PTR RRset for the leased address.
165	 */
166	updrec = minires_mkupdrec (S_UPDATE,
167				   (const char *)ddns_rev_name -> data,
168				   C_IN, T_PTR, 0);
169	if (!updrec) {
170		result = ISC_R_NOMEMORY;
171		goto error;
172	}
173
174	updrec -> r_data = (unsigned char *)0;
175	updrec -> r_size = 0;
176	updrec -> r_opcode = DELETE;
177
178	ISC_LIST_APPEND (updqueue, updrec, r_link);
179
180	/*
181	 * Attempt to perform the update.
182	 */
183	result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
184#if defined (DEBUG)
185	print_dns_status ((int)result, &updqueue);
186#endif
187	if (result == ISC_R_SUCCESS) {
188		log_info ("removed reverse map on %.*s",
189			  (int)ddns_rev_name -> len,
190			  (const char *)ddns_rev_name -> data);
191	} else {
192		if (result != ISC_R_NXRRSET && result != ISC_R_NXDOMAIN)
193			log_error ("can't remove reverse map on %.*s: %s",
194				   (int)ddns_rev_name -> len,
195				   (const char *)ddns_rev_name -> data,
196				   isc_result_totext (result));
197	}
198
199	/* Not there is success. */
200	if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
201		result = ISC_R_SUCCESS;
202
203	/* Fall through. */
204      error:
205
206	while (!ISC_LIST_EMPTY (updqueue)) {
207		updrec = ISC_LIST_HEAD (updqueue);
208		ISC_LIST_UNLINK (updqueue, updrec, r_link);
209		minires_freeupdrec (updrec);
210	}
211
212	return result;
213}
214
215
216int ddns_updates (struct packet *packet,
217		  struct lease *lease, struct lease *old,
218		  struct lease_state *state)
219{
220	unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
221	struct data_string ddns_hostname;
222	struct data_string ddns_domainname;
223	struct data_string old_ddns_fwd_name;
224	struct data_string ddns_fwd_name;
225	struct data_string ddns_rev_name;
226	struct data_string ddns_dhcid;
227	struct data_string d1;
228	struct option_cache *oc;
229	int s1, s2;
230	int result = 0;
231	isc_result_t rcode1 = ISC_R_SUCCESS, rcode2 = ISC_R_SUCCESS;
232	int server_updates_a = 1;
233	struct buffer *bp = (struct buffer *)0;
234	int ignorep = 0;
235
236	s1 = 0;		/* XXXGCC -Wuninitialized [arm / sparc64] */
237
238	if (ddns_update_style != 2)
239		return 0;
240
241	/* Can only cope with IPv4 addrs at the moment. */
242	if (lease -> ip_addr . len != 4)
243		return 0;
244
245	memset (&ddns_hostname, 0, sizeof (ddns_hostname));
246	memset (&ddns_domainname, 0, sizeof (ddns_domainname));
247	memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
248	memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
249	memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
250	memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
251
252	/* If we are allowed to accept the client's update of its own A
253	   record, see if the client wants to update its own A record. */
254	if (!(oc = lookup_option (&server_universe, state -> options,
255				  SV_CLIENT_UPDATES)) ||
256	    evaluate_boolean_option_cache (&ignorep, packet, lease,
257					   (struct client_state *)0,
258					   packet -> options,
259					   state -> options,
260					   &lease -> scope, oc, MDL)) {
261		/* If there's no fqdn.no-client-update or if it's
262		   nonzero, don't try to use the client-supplied
263		   XXX */
264		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
265					  FQDN_SERVER_UPDATE)) ||
266		    evaluate_boolean_option_cache (&ignorep, packet, lease,
267						   (struct client_state *)0,
268						   packet -> options,
269						   state -> options,
270						   &lease -> scope, oc, MDL))
271			goto noclient;
272		/* Win98 and Win2k will happily claim to be willing to
273		   update an unqualified domain name. */
274		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
275					  FQDN_DOMAINNAME)))
276			goto noclient;
277		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
278					  FQDN_FQDN)) ||
279		    !evaluate_option_cache (&ddns_fwd_name, packet, lease,
280					    (struct client_state *)0,
281					    packet -> options,
282					    state -> options,
283					    &lease -> scope, oc, MDL))
284			goto noclient;
285		server_updates_a = 0;
286		goto client_updates;
287	}
288      noclient:
289	/* If do-forward-updates is disabled, this basically means don't
290	   do an update unless the client is participating, so if we get
291	   here and do-forward-updates is disabled, we can stop. */
292	if ((oc = lookup_option (&server_universe, state -> options,
293				 SV_DO_FORWARD_UPDATES)) &&
294	    !evaluate_boolean_option_cache (&ignorep, packet, lease,
295					    (struct client_state *)0,
296					    packet -> options,
297					    state -> options,
298					    &lease -> scope, oc, MDL)) {
299		return 0;
300	}
301
302	/* If it's a static lease, then don't do the DNS update unless we're
303	   specifically configured to do so.   If the client asked to do its
304	   own update and we allowed that, we don't do this test. */
305	if (lease -> flags & STATIC_LEASE) {
306		if (!(oc = lookup_option (&server_universe, state -> options,
307					  SV_UPDATE_STATIC_LEASES)) ||
308		    !evaluate_boolean_option_cache (&ignorep, packet, lease,
309						    (struct client_state *)0,
310						    packet -> options,
311						    state -> options,
312						    &lease -> scope, oc, MDL))
313			return 0;
314	}
315
316	/*
317	 * Compute the name for the A record.
318	 */
319	oc = lookup_option (&server_universe, state -> options,
320			    SV_DDNS_HOST_NAME);
321	if (oc)
322		s1 = evaluate_option_cache (&ddns_hostname, packet, lease,
323					    (struct client_state *)0,
324					    packet -> options,
325					    state -> options,
326					    &lease -> scope, oc, MDL);
327	else
328		s1 = 0;
329
330	oc = lookup_option (&server_universe, state -> options,
331			    SV_DDNS_DOMAIN_NAME);
332	if (oc)
333		s2 = evaluate_option_cache (&ddns_domainname, packet, lease,
334					    (struct client_state *)0,
335					    packet -> options,
336					    state -> options,
337					    &lease -> scope, oc, MDL);
338	else
339		s2 = 0;
340
341	if (s1 && s2) {
342		if (ddns_hostname.len + ddns_domainname.len > 253) {
343			log_error ("ddns_update: host.domain name too long");
344
345			goto out;
346		}
347
348		buffer_allocate (&ddns_fwd_name.buffer,
349				 ddns_hostname.len + ddns_domainname.len + 2,
350				 MDL);
351		if (ddns_fwd_name.buffer) {
352			ddns_fwd_name.data = ddns_fwd_name.buffer -> data;
353			data_string_append (&ddns_fwd_name, &ddns_hostname);
354			ddns_fwd_name.buffer -> data [ddns_fwd_name.len] = '.';
355			ddns_fwd_name.len++;
356			data_string_append (&ddns_fwd_name, &ddns_domainname);
357			ddns_fwd_name.buffer -> data [ddns_fwd_name.len] ='\0';
358			ddns_fwd_name.terminated = 1;
359		}
360	}
361      client_updates:
362
363	/* See if there's a name already stored on the lease. */
364	if (find_bound_string (&old_ddns_fwd_name,
365			       lease -> scope, "ddns-fwd-name")) {
366		/* If there is, see if it's different. */
367		if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
368		    memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
369			    old_ddns_fwd_name.len)) {
370			/* If the name is different, try to delete
371			   the old A record. */
372			if (!ddns_removals (lease))
373				goto out;
374			/* If the delete succeeded, go install the new
375			   record. */
376			goto in;
377		}
378
379		/* See if there's a DHCID on the lease. */
380		if (!find_bound_string (&ddns_dhcid,
381					lease -> scope, "ddns-txt")) {
382			/* If there's no DHCID, the update was probably
383			   done with the old-style ad-hoc DDNS updates.
384			   So if the expiry and release events look like
385			   they're the same, run them.   This should delete
386			   the old DDNS data. */
387			if (old -> on_expiry == old -> on_release) {
388				execute_statements ((struct binding_value **)0,
389						    (struct packet *)0, lease,
390						    (struct client_state *)0,
391						    (struct option_state *)0,
392						    (struct option_state *)0,
393						    &lease -> scope,
394						    old -> on_expiry);
395				if (old -> on_expiry)
396					executable_statement_dereference
397						(&old -> on_expiry, MDL);
398				if (old -> on_release)
399					executable_statement_dereference
400						(&old -> on_release, MDL);
401				/* Now, install the DDNS data the new way. */
402				goto in;
403			}
404		}
405
406		/* See if the administrator wants to do updates even
407		   in cases where the update already appears to have been
408		   done. */
409		if (!(oc = lookup_option (&server_universe, state -> options,
410					  SV_UPDATE_OPTIMIZATION)) ||
411		    evaluate_boolean_option_cache (&ignorep, packet, lease,
412						   (struct client_state *)0,
413						   packet -> options,
414						   state -> options,
415						   &lease -> scope, oc, MDL)) {
416			result = 1;
417			goto noerror;
418		}
419	}
420
421	/* If there's no ddns-fwd-name on the lease, see if there's
422	   a ddns-client-fqdn, indicating a prior client FQDN update.
423	   If there is, and if we're still doing the client update,
424	   see if the name has changed.   If it hasn't, don't do the
425	   PTR update. */
426	if (find_bound_string (&old_ddns_fwd_name,
427			       lease -> scope, "ddns-client-fqdn")) {
428		/* If the name is not different, no need to update
429		   the PTR record. */
430		if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
431		    !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
432			     old_ddns_fwd_name.len) &&
433		    (!(oc = lookup_option (&server_universe,
434					   state -> options,
435					   SV_UPDATE_OPTIMIZATION)) ||
436		     evaluate_boolean_option_cache (&ignorep, packet, lease,
437						    (struct client_state *)0,
438						    packet -> options,
439						    state -> options,
440						    &lease -> scope, oc,
441						    MDL))) {
442			goto noerror;
443		}
444	}
445      in:
446
447	/* If we don't have a name that the client has been assigned, we
448	   can just skip all this. */
449	if (!ddns_fwd_name.len)
450		goto out;
451
452	if (ddns_fwd_name.len > 255) {
453		log_error ("client provided fqdn: too long");
454		goto out;
455	}
456
457	/*
458	 * Compute the RR TTL.
459	 */
460	ddns_ttl = DEFAULT_DDNS_TTL;
461	memset (&d1, 0, sizeof d1);
462	if ((oc = lookup_option (&server_universe, state -> options,
463				 SV_DDNS_TTL))) {
464		if (evaluate_option_cache (&d1, packet, lease,
465					   (struct client_state *)0,
466					   packet -> options,
467					   state -> options,
468					   &lease -> scope, oc, MDL)) {
469			if (d1.len == sizeof (u_int32_t))
470				ddns_ttl = getULong (d1.data);
471			data_string_forget (&d1, MDL);
472		}
473	}
474
475
476	/*
477	 * Compute the reverse IP name.
478	 */
479	oc = lookup_option (&server_universe, state -> options,
480			    SV_DDNS_REV_DOMAIN_NAME);
481	if (oc)
482		s1 = evaluate_option_cache (&d1, packet, lease,
483					    (struct client_state *)0,
484					    packet -> options,
485					    state -> options,
486					    &lease -> scope, oc, MDL);
487	else
488		s1 = 0;
489
490	if (s1 && (d1.len > 238)) {
491		log_error ("ddns_update: Calculated rev domain name too long.");
492		s1 = 0;
493		data_string_forget (&d1, MDL);
494	}
495
496	if (oc && s1) {
497		/* Buffer length:
498		   XXX.XXX.XXX.XXX.<ddns-rev-domain-name>\0 */
499		buffer_allocate (&ddns_rev_name.buffer,
500				 d1.len + 17, MDL);
501		if (ddns_rev_name.buffer) {
502			ddns_rev_name.data = ddns_rev_name.buffer -> data;
503
504			/* %Audit% Cannot exceed 17 bytes. %2004.06.17,Safe% */
505			sprintf ((char *)ddns_rev_name.buffer -> data,
506				  "%u.%u.%u.%u.",
507				  lease -> ip_addr . iabuf[3] & 0xff,
508				  lease -> ip_addr . iabuf[2] & 0xff,
509				  lease -> ip_addr . iabuf[1] & 0xff,
510				  lease -> ip_addr . iabuf[0] & 0xff);
511
512			ddns_rev_name.len =
513				strlen ((const char *)ddns_rev_name.data);
514			data_string_append (&ddns_rev_name, &d1);
515			ddns_rev_name.buffer -> data [ddns_rev_name.len] ='\0';
516			ddns_rev_name.terminated = 1;
517		}
518
519		data_string_forget (&d1, MDL);
520	}
521
522	/*
523	 * If we are updating the A record, compute the DHCID value.
524	 */
525	if (server_updates_a) {
526		memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
527		if (lease -> uid && lease -> uid_len)
528			result = get_dhcid (&ddns_dhcid,
529					    DHO_DHCP_CLIENT_IDENTIFIER,
530					    lease -> uid, lease -> uid_len);
531		else
532			result = get_dhcid (&ddns_dhcid, 0,
533					    lease -> hardware_addr.hbuf,
534					    lease -> hardware_addr.hlen);
535		if (!result)
536			goto badfqdn;
537	}
538
539	/*
540	 * Start the resolver, if necessary.
541	 */
542	if (!resolver_inited) {
543		minires_ninit (&resolver_state);
544		resolver_inited = 1;
545		resolver_state.retrans = 1;
546		resolver_state.retry = 1;
547	}
548
549	/*
550	 * Perform updates.
551	 */
552	if (ddns_fwd_name.len && ddns_dhcid.len)
553		rcode1 = ddns_update_a (&ddns_fwd_name, lease -> ip_addr,
554					&ddns_dhcid, ddns_ttl, 0);
555
556	if (rcode1 == ISC_R_SUCCESS) {
557		if (ddns_fwd_name.len && ddns_rev_name.len)
558			rcode2 = ddns_update_ptr (&ddns_fwd_name,
559						  &ddns_rev_name, ddns_ttl);
560	} else
561		rcode2 = rcode1;
562
563	if (rcode1 == ISC_R_SUCCESS &&
564	    (server_updates_a || rcode2 == ISC_R_SUCCESS)) {
565		bind_ds_value (&lease -> scope,
566			       (server_updates_a
567				? "ddns-fwd-name" : "ddns-client-fqdn"),
568			       &ddns_fwd_name);
569		if (server_updates_a)
570			bind_ds_value (&lease -> scope, "ddns-txt",
571				       &ddns_dhcid);
572	}
573
574	if (rcode2 == ISC_R_SUCCESS) {
575		bind_ds_value (&lease -> scope, "ddns-rev-name",
576			       &ddns_rev_name);
577	}
578
579	/* Set up the outgoing FQDN option if there was an incoming
580	   FQDN option.  If there's a valid FQDN option, there should
581	   be an FQDN_ENCODED suboption, so we test the latter to
582	   detect the presence of the former. */
583      noerror:
584	if ((oc = lookup_option (&fqdn_universe,
585				 packet -> options, FQDN_ENCODED))
586	    && buffer_allocate (&bp, ddns_fwd_name.len + 5, MDL)) {
587		bp -> data [0] = server_updates_a;
588		if (!save_option_buffer (&fqdn_universe, state -> options,
589					 bp, &bp -> data [0], 1,
590					 &fqdn_options [FQDN_SERVER_UPDATE],
591					 0))
592			goto badfqdn;
593		bp -> data [1] = server_updates_a;
594		if (!save_option_buffer (&fqdn_universe, state -> options,
595					 bp, &bp -> data [1], 1,
596					 &fqdn_options [FQDN_NO_CLIENT_UPDATE],
597					 0))
598			goto badfqdn;
599		/* Do the same encoding the client did. */
600		oc = lookup_option (&fqdn_universe, packet -> options,
601				    FQDN_ENCODED);
602		if (oc &&
603		    evaluate_boolean_option_cache (&ignorep, packet, lease,
604						   (struct client_state *)0,
605						   packet -> options,
606						   state -> options,
607						   &lease -> scope, oc, MDL))
608			bp -> data [2] = 1;
609		else
610			bp -> data [2] = 0;
611		if (!save_option_buffer (&fqdn_universe, state -> options,
612					 bp, &bp -> data [2], 1,
613					 &fqdn_options [FQDN_ENCODED],
614					 0))
615			goto badfqdn;
616		bp -> data [3] = isc_rcode_to_ns (rcode1);
617		if (!save_option_buffer (&fqdn_universe, state -> options,
618					 bp, &bp -> data [3], 1,
619					 &fqdn_options [FQDN_RCODE1],
620					 0))
621			goto badfqdn;
622		bp -> data [4] = isc_rcode_to_ns (rcode2);
623		if (!save_option_buffer (&fqdn_universe, state -> options,
624					 bp, &bp -> data [4], 1,
625					 &fqdn_options [FQDN_RCODE2],
626					 0))
627			goto badfqdn;
628		if (ddns_fwd_name.len) {
629		    memcpy (&bp -> data [5],
630			    ddns_fwd_name.data, ddns_fwd_name.len);
631		    if (!save_option_buffer (&fqdn_universe, state -> options,
632					     bp, &bp -> data [5],
633					     ddns_fwd_name.len,
634					     &fqdn_options [FQDN_FQDN],
635					     0))
636			goto badfqdn;
637		}
638	}
639
640      badfqdn:
641      out:
642	/*
643	 * Final cleanup.
644	 */
645	data_string_forget (&ddns_hostname, MDL);
646	data_string_forget (&ddns_domainname, MDL);
647	data_string_forget (&old_ddns_fwd_name, MDL);
648	data_string_forget (&ddns_fwd_name, MDL);
649	data_string_forget (&ddns_rev_name, MDL);
650	data_string_forget (&ddns_dhcid, MDL);
651	if (bp)
652		buffer_dereference (&bp, MDL);
653
654	return result;
655}
656
657int ddns_removals (struct lease *lease)
658{
659	struct data_string ddns_fwd_name;
660	struct data_string ddns_rev_name;
661	struct data_string ddns_dhcid;
662	isc_result_t rcode;
663	int result = 0;
664	int client_updated = 0;
665
666	/* No scope implies that DDNS has not been performed for this lease. */
667	if (!lease -> scope)
668		return 0;
669
670	if (ddns_update_style != 2)
671		return 0;
672
673	/*
674	 * Look up stored names.
675	 */
676	memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
677	memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
678	memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
679
680	/*
681	 * Start the resolver, if necessary.
682	 */
683	if (!resolver_inited) {
684		minires_ninit (&resolver_state);
685		resolver_inited = 1;
686		resolver_state.retrans = 1;
687		resolver_state.retry = 1;
688	}
689
690	/* We need the fwd name whether we are deleting both records or just
691	   the PTR record, so if it's not there, we can't proceed. */
692	if (!find_bound_string (&ddns_fwd_name,
693				lease -> scope, "ddns-fwd-name")) {
694		/* If there's no ddns-fwd-name, look for the client fqdn,
695		   in case the client did the update. */
696		if (!find_bound_string (&ddns_fwd_name,
697					lease -> scope, "ddns-client-fqdn"))
698			goto try_rev;
699		client_updated = 1;
700		goto try_rev;
701	}
702
703	/* If the ddns-txt binding isn't there, this isn't an interim
704	   or rfc3??? record, so we can't delete the A record using
705	   this mechanism, but we can delete the PTR record. */
706	if (!find_bound_string (&ddns_dhcid, lease -> scope, "ddns-txt")) {
707		result = 1;
708		goto try_rev;
709	}
710
711	/*
712	 * Perform removals.
713	 */
714	if (ddns_fwd_name.len)
715		rcode = ddns_remove_a (&ddns_fwd_name,
716				       lease -> ip_addr, &ddns_dhcid);
717	else
718		rcode = ISC_R_SUCCESS;
719
720	if (rcode == ISC_R_SUCCESS) {
721		result = 1;
722		unset (lease -> scope, "ddns-fwd-name");
723		unset (lease -> scope, "ddns-txt");
724	      try_rev:
725		if (find_bound_string (&ddns_rev_name,
726				       lease -> scope, "ddns-rev-name")) {
727			if ((ns_rcode)ddns_remove_ptr(&ddns_rev_name) == NOERROR) {
728				unset (lease -> scope, "ddns-rev-name");
729				if (client_updated)
730					unset (lease -> scope,
731					       "ddns-client-fqdn");
732				/* XXX this is to compensate for a bug in
733				   XXX 3.0rc8, and should be removed before
734				   XXX 3.0pl1. */
735				else if (!ddns_fwd_name.len)
736					unset (lease -> scope, "ddns-text");
737			} else
738				result = 0;
739		}
740	}
741
742	data_string_forget (&ddns_fwd_name, MDL);
743	data_string_forget (&ddns_rev_name, MDL);
744	data_string_forget (&ddns_dhcid, MDL);
745
746	return result;
747}
748
749#endif /* NSUPDATE */
750