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