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