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