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