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