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