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