Flesh out BUF_CMD_FLUSH support.
[dragonfly.git] / contrib / dhcp-3.0 / server / ddns.c
1 /* ddns.c
2
3    Dynamic DNS updates. */
4
5 /*
6  * Copyright (c) 2004 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
36 static char copyright[] =
37 "$Id: ddns.c,v 1.15.2.15 2004/11/24 17:39:18 dhankins Exp $ Copyright (c) 2004 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.   :') */
50 static 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
59 static 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
146 static 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
216 int 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         unsigned len;
228         struct data_string d1;
229         struct option_cache *oc;
230         int s1, s2;
231         int result = 0;
232         isc_result_t rcode1 = ISC_R_SUCCESS, rcode2 = ISC_R_SUCCESS;
233         int server_updates_a = 1;
234         struct buffer *bp = (struct buffer *)0;
235         int ignorep = 0;
236
237         if (ddns_update_style != 2)
238                 return 0;
239
240         /* Can only cope with IPv4 addrs at the moment. */
241         if (lease -> ip_addr . len != 4)
242                 return 0;
243
244         memset (&ddns_hostname, 0, sizeof (ddns_hostname));
245         memset (&ddns_domainname, 0, sizeof (ddns_domainname));
246         memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
247         memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
248         memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
249         memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
250
251         /* If we are allowed to accept the client's update of its own A
252            record, see if the client wants to update its own A record. */
253         if (!(oc = lookup_option (&server_universe, state -> options,
254                                   SV_CLIENT_UPDATES)) ||
255             evaluate_boolean_option_cache (&ignorep, packet, lease,
256                                            (struct client_state *)0,
257                                            packet -> options,
258                                            state -> options,
259                                            &lease -> scope, oc, MDL)) {
260                 /* If there's no fqdn.no-client-update or if it's
261                    nonzero, don't try to use the client-supplied
262                    XXX */
263                 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
264                                           FQDN_SERVER_UPDATE)) ||
265                     evaluate_boolean_option_cache (&ignorep, packet, lease,
266                                                    (struct client_state *)0,
267                                                    packet -> options,
268                                                    state -> options,
269                                                    &lease -> scope, oc, MDL))
270                         goto noclient;
271                 /* Win98 and Win2k will happily claim to be willing to
272                    update an unqualified domain name. */
273                 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
274                                           FQDN_DOMAINNAME)))
275                         goto noclient;
276                 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
277                                           FQDN_FQDN)) ||
278                     !evaluate_option_cache (&ddns_fwd_name, packet, lease,
279                                             (struct client_state *)0,
280                                             packet -> options,
281                                             state -> options,
282                                             &lease -> scope, oc, MDL))
283                         goto noclient;
284                 server_updates_a = 0;
285                 goto client_updates;
286         }
287       noclient:
288         /* If do-forward-updates is disabled, this basically means don't
289            do an update unless the client is participating, so if we get
290            here and do-forward-updates is disabled, we can stop. */
291         if ((oc = lookup_option (&server_universe, state -> options,
292                                  SV_DO_FORWARD_UPDATES)) &&
293             !evaluate_boolean_option_cache (&ignorep, packet, lease,
294                                             (struct client_state *)0,
295                                             packet -> options,
296                                             state -> options,
297                                             &lease -> scope, oc, MDL)) {
298                 return 0;
299         }
300
301         /* If it's a static lease, then don't do the DNS update unless we're
302            specifically configured to do so.   If the client asked to do its
303            own update and we allowed that, we don't do this test. */
304         if (lease -> flags & STATIC_LEASE) {
305                 if (!(oc = lookup_option (&server_universe, state -> options,
306                                           SV_UPDATE_STATIC_LEASES)) ||
307                     !evaluate_boolean_option_cache (&ignorep, packet, lease,
308                                                     (struct client_state *)0,
309                                                     packet -> options,
310                                                     state -> options,
311                                                     &lease -> scope, oc, MDL))
312                         return 0;
313         }
314
315         /*
316          * Compute the name for the A record.
317          */
318         oc = lookup_option (&server_universe, state -> options,
319                             SV_DDNS_HOST_NAME);
320         if (oc)
321                 s1 = evaluate_option_cache (&ddns_hostname, packet, lease,
322                                             (struct client_state *)0,
323                                             packet -> options,
324                                             state -> options,
325                                             &lease -> scope, oc, MDL);
326         else
327                 s1 = 0;
328
329         oc = lookup_option (&server_universe, state -> options,
330                             SV_DDNS_DOMAIN_NAME);
331         if (oc)
332                 s2 = evaluate_option_cache (&ddns_domainname, packet, lease,
333                                             (struct client_state *)0,
334                                             packet -> options,
335                                             state -> options,
336                                             &lease -> scope, oc, MDL);
337         else
338                 s2 = 0;
339
340         if (s1 && s2) {
341                 if (ddns_hostname.len + ddns_domainname.len > 253) {
342                         log_error ("ddns_update: host.domain name too long");
343
344                         goto out;
345                 }
346
347                 buffer_allocate (&ddns_fwd_name.buffer,
348                                  ddns_hostname.len + ddns_domainname.len + 2,
349                                  MDL);
350                 if (ddns_fwd_name.buffer) {
351                         ddns_fwd_name.data = ddns_fwd_name.buffer -> data;
352                         data_string_append (&ddns_fwd_name, &ddns_hostname);
353                         ddns_fwd_name.buffer -> data [ddns_fwd_name.len] = '.';
354                         ddns_fwd_name.len++;
355                         data_string_append (&ddns_fwd_name, &ddns_domainname);
356                         ddns_fwd_name.buffer -> data [ddns_fwd_name.len] ='\0';
357                         ddns_fwd_name.terminated = 1;
358                 }
359         }
360       client_updates:
361
362         /* See if there's a name already stored on the lease. */
363         if (find_bound_string (&old_ddns_fwd_name,
364                                lease -> scope, "ddns-fwd-name")) {
365                 /* If there is, see if it's different. */
366                 if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
367                     memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
368                             old_ddns_fwd_name.len)) {
369                         /* If the name is different, try to delete
370                            the old A record. */
371                         if (!ddns_removals (lease))
372                                 goto out;
373                         /* If the delete succeeded, go install the new
374                            record. */
375                         goto in;
376                 }
377
378                 /* See if there's a DHCID on the lease. */
379                 if (!find_bound_string (&ddns_dhcid,
380                                         lease -> scope, "ddns-txt")) {
381                         /* If there's no DHCID, the update was probably
382                            done with the old-style ad-hoc DDNS updates.
383                            So if the expiry and release events look like
384                            they're the same, run them.   This should delete
385                            the old DDNS data. */
386                         if (old -> on_expiry == old -> on_release) {
387                                 execute_statements ((struct binding_value **)0,
388                                                     (struct packet *)0, lease,
389                                                     (struct client_state *)0,
390                                                     (struct option_state *)0,
391                                                     (struct option_state *)0,
392                                                     &lease -> scope,
393                                                     old -> on_expiry);
394                                 if (old -> on_expiry)
395                                         executable_statement_dereference
396                                                 (&old -> on_expiry, MDL);
397                                 if (old -> on_release)
398                                         executable_statement_dereference
399                                                 (&old -> on_release, MDL);
400                                 /* Now, install the DDNS data the new way. */
401                                 goto in;
402                         }
403                 }
404
405                 /* See if the administrator wants to do updates even
406                    in cases where the update already appears to have been
407                    done. */
408                 if (!(oc = lookup_option (&server_universe, state -> options,
409                                           SV_UPDATE_OPTIMIZATION)) ||
410                     evaluate_boolean_option_cache (&ignorep, packet, lease,
411                                                    (struct client_state *)0,
412                                                    packet -> options,
413                                                    state -> options,
414                                                    &lease -> scope, oc, MDL)) {
415                         result = 1;
416                         goto noerror;
417                 }
418         }
419
420         /* If there's no ddns-fwd-name on the lease, see if there's
421            a ddns-client-fqdn, indicating a prior client FQDN update.
422            If there is, and if we're still doing the client update,
423            see if the name has changed.   If it hasn't, don't do the
424            PTR update. */
425         if (find_bound_string (&old_ddns_fwd_name,
426                                lease -> scope, "ddns-client-fqdn")) {
427                 /* If the name is not different, no need to update
428                    the PTR record. */
429                 if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
430                     !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
431                              old_ddns_fwd_name.len) &&
432                     (!(oc = lookup_option (&server_universe,
433                                            state -> options,
434                                            SV_UPDATE_OPTIMIZATION)) ||
435                      evaluate_boolean_option_cache (&ignorep, packet, lease,
436                                                     (struct client_state *)0,
437                                                     packet -> options,
438                                                     state -> options,
439                                                     &lease -> scope, oc,
440                                                     MDL))) {
441                         goto noerror;
442                 }
443         }
444       in:
445                 
446         /* If we don't have a name that the client has been assigned, we
447            can just skip all this. */
448         if (!ddns_fwd_name.len)
449                 goto out;
450
451         if (ddns_fwd_name.len > 255) {
452                 log_error ("client provided fqdn: too long");
453                 goto out;
454         }
455
456         /*
457          * Compute the RR TTL.
458          */
459         ddns_ttl = DEFAULT_DDNS_TTL;
460         memset (&d1, 0, sizeof d1);
461         if ((oc = lookup_option (&server_universe, state -> options,
462                                  SV_DDNS_TTL))) {
463                 if (evaluate_option_cache (&d1, packet, lease,
464                                            (struct client_state *)0,
465                                            packet -> options,
466                                            state -> options,
467                                            &lease -> scope, oc, MDL)) {
468                         if (d1.len == sizeof (u_int32_t))
469                                 ddns_ttl = getULong (d1.data);
470                         data_string_forget (&d1, MDL);
471                 }
472         }
473
474
475         /*
476          * Compute the reverse IP name.
477          */
478         oc = lookup_option (&server_universe, state -> options,
479                             SV_DDNS_REV_DOMAIN_NAME);
480         if (oc)
481                 s1 = evaluate_option_cache (&d1, packet, lease,
482                                             (struct client_state *)0,
483                                             packet -> options,
484                                             state -> options,
485                                             &lease -> scope, oc, MDL);
486         else
487                 s1 = 0;
488
489         if (s1 && (d1.len > 238)) {
490                 log_error ("ddns_update: Calculated rev domain name too long.");
491                 s1 = 0;
492                 data_string_forget (&d1, MDL);
493         }
494
495         if (oc && s1) {
496                 /* Buffer length:
497                    XXX.XXX.XXX.XXX.<ddns-rev-domain-name>\0 */
498                 buffer_allocate (&ddns_rev_name.buffer,
499                                  d1.len + 17, MDL);
500                 if (ddns_rev_name.buffer) {
501                         ddns_rev_name.data = ddns_rev_name.buffer -> data;
502
503                         /* %Audit% Cannot exceed 17 bytes. %2004.06.17,Safe% */
504                         sprintf ((char *)ddns_rev_name.buffer -> data,
505                                   "%u.%u.%u.%u.",
506                                   lease -> ip_addr . iabuf[3] & 0xff,
507                                   lease -> ip_addr . iabuf[2] & 0xff,
508                                   lease -> ip_addr . iabuf[1] & 0xff,
509                                   lease -> ip_addr . iabuf[0] & 0xff);
510
511                         ddns_rev_name.len =
512                                 strlen ((const char *)ddns_rev_name.data);
513                         data_string_append (&ddns_rev_name, &d1);
514                         ddns_rev_name.buffer -> data [ddns_rev_name.len] ='\0';
515                         ddns_rev_name.terminated = 1;
516                 }
517                 
518                 data_string_forget (&d1, MDL);
519         }
520
521         /*
522          * If we are updating the A record, compute the DHCID value.
523          */
524         if (server_updates_a) {
525                 memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
526                 if (lease -> uid && lease -> uid_len)
527                         result = get_dhcid (&ddns_dhcid,
528                                             DHO_DHCP_CLIENT_IDENTIFIER,
529                                             lease -> uid, lease -> uid_len);
530                 else
531                         result = get_dhcid (&ddns_dhcid, 0,
532                                             lease -> hardware_addr.hbuf,
533                                             lease -> hardware_addr.hlen);
534                 if (!result)
535                         goto badfqdn;
536         }
537
538         /*
539          * Start the resolver, if necessary.
540          */
541         if (!resolver_inited) {
542                 minires_ninit (&resolver_state);
543                 resolver_inited = 1;
544                 resolver_state.retrans = 1;
545                 resolver_state.retry = 1;
546         }
547
548         /*
549          * Perform updates.
550          */
551         if (ddns_fwd_name.len && ddns_dhcid.len)
552                 rcode1 = ddns_update_a (&ddns_fwd_name, lease -> ip_addr,
553                                         &ddns_dhcid, ddns_ttl, 0);
554         
555         if (rcode1 == ISC_R_SUCCESS) {
556                 if (ddns_fwd_name.len && ddns_rev_name.len)
557                         rcode2 = ddns_update_ptr (&ddns_fwd_name,
558                                                   &ddns_rev_name, ddns_ttl);
559         } else
560                 rcode2 = rcode1;
561
562         if (rcode1 == ISC_R_SUCCESS &&
563             (server_updates_a || rcode2 == ISC_R_SUCCESS)) {
564                 bind_ds_value (&lease -> scope, 
565                                (server_updates_a
566                                 ? "ddns-fwd-name" : "ddns-client-fqdn"),
567                                &ddns_fwd_name);
568                 if (server_updates_a)
569                         bind_ds_value (&lease -> scope, "ddns-txt",
570                                        &ddns_dhcid);
571         }
572
573         if (rcode2 == ISC_R_SUCCESS) {
574                 bind_ds_value (&lease -> scope, "ddns-rev-name",
575                                &ddns_rev_name);
576         }
577
578         /* Set up the outgoing FQDN option if there was an incoming
579            FQDN option.  If there's a valid FQDN option, there should
580            be an FQDN_ENCODED suboption, so we test the latter to
581            detect the presence of the former. */
582       noerror:
583         if ((oc = lookup_option (&fqdn_universe,
584                                  packet -> options, FQDN_ENCODED))
585             && buffer_allocate (&bp, ddns_fwd_name.len + 5, MDL)) {
586                 bp -> data [0] = server_updates_a;
587                 if (!save_option_buffer (&fqdn_universe, state -> options,
588                                          bp, &bp -> data [0], 1,
589                                          &fqdn_options [FQDN_SERVER_UPDATE],
590                                          0))
591                         goto badfqdn;
592                 bp -> data [1] = server_updates_a;
593                 if (!save_option_buffer (&fqdn_universe, state -> options,
594                                          bp, &bp -> data [1], 1,
595                                          &fqdn_options [FQDN_NO_CLIENT_UPDATE],
596                                          0))
597                         goto badfqdn;
598                 /* Do the same encoding the client did. */
599                 oc = lookup_option (&fqdn_universe, packet -> options,
600                                     FQDN_ENCODED);
601                 if (oc &&
602                     evaluate_boolean_option_cache (&ignorep, packet, lease,
603                                                    (struct client_state *)0,
604                                                    packet -> options,
605                                                    state -> options,
606                                                    &lease -> scope, oc, MDL))
607                         bp -> data [2] = 1;
608                 else
609                         bp -> data [2] = 0;
610                 if (!save_option_buffer (&fqdn_universe, state -> options,
611                                          bp, &bp -> data [2], 1,
612                                          &fqdn_options [FQDN_ENCODED],
613                                          0))
614                         goto badfqdn;
615                 bp -> data [3] = isc_rcode_to_ns (rcode1);
616                 if (!save_option_buffer (&fqdn_universe, state -> options,
617                                          bp, &bp -> data [3], 1,
618                                          &fqdn_options [FQDN_RCODE1],
619                                          0))
620                         goto badfqdn;
621                 bp -> data [4] = isc_rcode_to_ns (rcode2);
622                 if (!save_option_buffer (&fqdn_universe, state -> options,
623                                          bp, &bp -> data [4], 1,
624                                          &fqdn_options [FQDN_RCODE2],
625                                          0))
626                         goto badfqdn;
627                 if (ddns_fwd_name.len) {
628                     memcpy (&bp -> data [5],
629                             ddns_fwd_name.data, ddns_fwd_name.len);
630                     if (!save_option_buffer (&fqdn_universe, state -> options,
631                                              bp, &bp -> data [5],
632                                              ddns_fwd_name.len,
633                                              &fqdn_options [FQDN_FQDN],
634                                              0))
635                         goto badfqdn;
636                 }
637         }
638
639       badfqdn:
640       out:
641         /*
642          * Final cleanup.
643          */
644         data_string_forget (&ddns_hostname, MDL);
645         data_string_forget (&ddns_domainname, MDL);
646         data_string_forget (&old_ddns_fwd_name, MDL);
647         data_string_forget (&ddns_fwd_name, MDL);
648         data_string_forget (&ddns_rev_name, MDL);
649         data_string_forget (&ddns_dhcid, MDL);
650         if (bp)
651                 buffer_dereference (&bp, MDL);
652
653         return result;
654 }
655
656 int ddns_removals (struct lease *lease)
657 {
658         struct data_string ddns_fwd_name;
659         struct data_string ddns_rev_name;
660         struct data_string ddns_dhcid;
661         isc_result_t rcode;
662         struct binding *binding;
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         }
687
688         /* We need the fwd name whether we are deleting both records or just
689            the PTR record, so if it's not there, we can't proceed. */
690         if (!find_bound_string (&ddns_fwd_name,
691                                 lease -> scope, "ddns-fwd-name")) {
692                 /* If there's no ddns-fwd-name, look for the client fqdn,
693                    in case the client did the update. */
694                 if (!find_bound_string (&ddns_fwd_name,
695                                         lease -> scope, "ddns-client-fqdn"))
696                         goto try_rev;
697                 client_updated = 1;
698                 goto try_rev;
699         }
700
701         /* If the ddns-txt binding isn't there, this isn't an interim
702            or rfc3??? record, so we can't delete the A record using
703            this mechanism, but we can delete the PTR record. */
704         if (!find_bound_string (&ddns_dhcid, lease -> scope, "ddns-txt")) {
705                 result = 1;
706                 goto try_rev;
707         }
708
709         /*
710          * Perform removals.
711          */
712         if (ddns_fwd_name.len)
713                 rcode = ddns_remove_a (&ddns_fwd_name,
714                                        lease -> ip_addr, &ddns_dhcid);
715         else
716                 rcode = ISC_R_SUCCESS;
717
718         if (rcode == ISC_R_SUCCESS) {
719                 result = 1;
720                 unset (lease -> scope, "ddns-fwd-name");
721                 unset (lease -> scope, "ddns-txt");
722               try_rev:
723                 if (find_bound_string (&ddns_rev_name,
724                                        lease -> scope, "ddns-rev-name")) {
725                         if (ddns_remove_ptr(&ddns_rev_name) == NOERROR) {
726                                 unset (lease -> scope, "ddns-rev-name");
727                                 if (client_updated)
728                                         unset (lease -> scope,
729                                                "ddns-client-fqdn");
730                                 /* XXX this is to compensate for a bug in
731                                    XXX 3.0rc8, and should be removed before
732                                    XXX 3.0pl1. */
733                                 else if (!ddns_fwd_name.len)
734                                         unset (lease -> scope, "ddns-text");
735                         } else
736                                 result = 0;
737                 }
738         }
739
740         data_string_forget (&ddns_fwd_name, MDL);
741         data_string_forget (&ddns_rev_name, MDL);
742         data_string_forget (&ddns_dhcid, MDL);
743
744         return result;
745 }
746
747 #endif /* NSUPDATE */