]> git.ipfire.org Git - thirdparty/dhcp.git/blame - server/ddns.c
Fix typo
[thirdparty/dhcp.git] / server / ddns.c
CommitLineData
b992d7e2
DN
1/* ddns.c
2
3 Dynamic DNS updates. */
4
5/*
4d2eaafb 6 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
98311e4b 7 * Copyright (c) 2000-2003 by Internet Software Consortium
b992d7e2 8 *
98311e4b
DH
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
b992d7e2 12 *
98311e4b
DH
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
b992d7e2 20 *
98311e4b
DH
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
b992d7e2 26 *
98311e4b 27 * This software has been donated to Internet Systems Consortium
0598e123
TL
28 * by Damien Neil of Nominum, Inc.
29 *
98311e4b 30 * To learn more about Internet Systems Consortium, see
0598e123 31 * ``http://www.isc.org/''. To learn more about Nominum, Inc., see
b992d7e2
DN
32 * ``http://www.nominum.com''.
33 */
34
b992d7e2 35#include "dhcpd.h"
37ee426a 36#include "dst/md5.h"
ec64b462 37#include "minires/minires.h"
b992d7e2
DN
38
39#ifdef NSUPDATE
40
0598e123
TL
41/* DN: No way of checking that there is enough space in a data_string's
42 buffer. Be certain to allocate enough!
385fcb27 43 TL: This is why the expression evaluation code allocates a *new*
0598e123 44 data_string. :') */
b992d7e2
DN
45static void data_string_append (struct data_string *ds1,
46 struct data_string *ds2)
47{
48 memcpy (ds1 -> buffer -> data + ds1 -> len,
49 ds2 -> data,
50 ds2 -> len);
51 ds1 -> len += ds2 -> len;
52}
53
5b77c910
TL
54static isc_result_t ddns_update_ptr (struct data_string *ddns_fwd_name,
55 struct data_string *ddns_rev_name,
56 unsigned long ttl)
b992d7e2 57{
385fcb27 58 ns_updque updqueue;
b992d7e2 59 ns_updrec *updrec;
c2318c20 60 isc_result_t result = ISC_R_UNEXPECTED;
b992d7e2
DN
61
62 /*
63 * The DHCP server submits a DNS query which deletes all of the PTR RRs
64 * associated with the lease IP address, and adds a PTR RR whose data
65 * is the client's (possibly disambiguated) host name. The server also
66 * adds a DHCID RR specified in Section 4.3.
67 * -- "Interaction between DHCP and DNS"
68 */
69
385fcb27 70 ISC_LIST_INIT (updqueue);
b992d7e2
DN
71
72 /*
73 * Delete all PTR RRs.
74 */
478028e7
TL
75 updrec = minires_mkupdrec (S_UPDATE,
76 (const char *)ddns_rev_name -> data,
b992d7e2 77 C_IN, T_PTR, 0);
ec64b462
TL
78 if (!updrec) {
79 result = ISC_R_NOMEMORY;
80 goto error;
81 }
b992d7e2 82
478028e7
TL
83 updrec -> r_data = (unsigned char *)0;
84 updrec -> r_size = 0;
b992d7e2
DN
85 updrec -> r_opcode = DELETE;
86
385fcb27 87 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 88
b992d7e2
DN
89 /*
90 * Add PTR RR.
91 */
478028e7
TL
92 updrec = minires_mkupdrec (S_UPDATE,
93 (const char *)ddns_rev_name -> data,
b992d7e2 94 C_IN, T_PTR, ttl);
ec64b462
TL
95 if (!updrec) {
96 result = ISC_R_NOMEMORY;
97 goto error;
98 }
b992d7e2 99
ee83afae 100 updrec -> r_data = ddns_fwd_name -> data;
478028e7 101 updrec -> r_size = ddns_fwd_name -> len;
b992d7e2
DN
102 updrec -> r_opcode = ADD;
103
385fcb27 104 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 105
b992d7e2
DN
106 /*
107 * Attempt to perform the update.
108 */
385fcb27 109 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
98311e4b 110#if defined (DEBUG)
385fcb27 111 print_dns_status ((int)result, &updqueue);
98311e4b
DH
112#endif
113 if (result == ISC_R_SUCCESS) {
114 log_info ("added reverse map from %.*s to %.*s",
115 (int)ddns_rev_name -> len,
116 (const char *)ddns_rev_name -> data,
117 (int)ddns_fwd_name -> len,
118 (const char *)ddns_fwd_name -> data);
119 } else {
120 log_error ("unable to add reverse map from %.*s to %.*s: %s",
121 (int)ddns_rev_name -> len,
122 (const char *)ddns_rev_name -> data,
123 (int)ddns_fwd_name -> len,
124 (const char *)ddns_fwd_name -> data,
125 isc_result_totext (result));
126 }
b992d7e2
DN
127
128 /* Fall through. */
ec64b462 129 error:
b992d7e2 130
385fcb27
TL
131 while (!ISC_LIST_EMPTY (updqueue)) {
132 updrec = ISC_LIST_HEAD (updqueue);
133 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
134 minires_freeupdrec (updrec);
135 }
136
137 return result;
138}
139
140
5b77c910 141static isc_result_t ddns_remove_ptr (struct data_string *ddns_rev_name)
b992d7e2 142{
385fcb27 143 ns_updque updqueue;
b992d7e2 144 ns_updrec *updrec;
ec64b462 145 isc_result_t result;
b992d7e2
DN
146
147 /*
148 * When a lease expires or a DHCP client issues a DHCPRELEASE request,
149 * the DHCP server SHOULD delete the PTR RR that matches the DHCP
150 * binding, if one was successfully added. The server's update query
151 * SHOULD assert that the name in the PTR record matches the name of
152 * the client whose lease has expired or been released.
153 * -- "Interaction between DHCP and DNS"
154 */
155
385fcb27 156 ISC_LIST_INIT (updqueue);
b992d7e2
DN
157
158 /*
ee83afae 159 * Delete the PTR RRset for the leased address.
b992d7e2 160 */
478028e7
TL
161 updrec = minires_mkupdrec (S_UPDATE,
162 (const char *)ddns_rev_name -> data,
b992d7e2 163 C_IN, T_PTR, 0);
ec64b462
TL
164 if (!updrec) {
165 result = ISC_R_NOMEMORY;
166 goto error;
167 }
b992d7e2 168
ee83afae
TL
169 updrec -> r_data = (unsigned char *)0;
170 updrec -> r_size = 0;
b992d7e2
DN
171 updrec -> r_opcode = DELETE;
172
385fcb27 173 ISC_LIST_APPEND (updqueue, updrec, r_link);
b992d7e2 174
b992d7e2
DN
175 /*
176 * Attempt to perform the update.
177 */
385fcb27 178 result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
98311e4b 179#if defined (DEBUG)
385fcb27 180 print_dns_status ((int)result, &updqueue);
98311e4b
DH
181#endif
182 if (result == ISC_R_SUCCESS) {
183 log_info ("removed reverse map on %.*s",
184 (int)ddns_rev_name -> len,
185 (const char *)ddns_rev_name -> data);
186 } else {
187 if (result != ISC_R_NXRRSET && result != ISC_R_NXDOMAIN)
188 log_error ("can't remove reverse map on %.*s: %s",
189 (int)ddns_rev_name -> len,
190 (const char *)ddns_rev_name -> data,
191 isc_result_totext (result));
192 }
193
194 /* Not there is success. */
195 if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
196 result = ISC_R_SUCCESS;
b992d7e2
DN
197
198 /* Fall through. */
ec64b462 199 error:
b992d7e2 200
385fcb27
TL
201 while (!ISC_LIST_EMPTY (updqueue)) {
202 updrec = ISC_LIST_HEAD (updqueue);
203 ISC_LIST_UNLINK (updqueue, updrec, r_link);
b992d7e2
DN
204 minires_freeupdrec (updrec);
205 }
206
207 return result;
208}
209
210
98bd7ca0
DH
211/* Determine what, if any, forward and reverse updates need to be
212 * performed, and carry them through.
213 */
214int
215ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
216 struct iaaddr *lease6, struct iaaddr *old6,
217 struct option_state *options)
b992d7e2
DN
218{
219 unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
220 struct data_string ddns_hostname;
221 struct data_string ddns_domainname;
478028e7 222 struct data_string old_ddns_fwd_name;
b992d7e2
DN
223 struct data_string ddns_fwd_name;
224 struct data_string ddns_rev_name;
b992d7e2 225 struct data_string ddns_dhcid;
98bd7ca0
DH
226 struct binding_scope **scope;
227 struct iaddr addr;
b992d7e2
DN
228 struct data_string d1;
229 struct option_cache *oc;
230 int s1, s2;
231 int result = 0;
ec64b462 232 isc_result_t rcode1 = ISC_R_SUCCESS, rcode2 = ISC_R_SUCCESS;
478028e7 233 int server_updates_a = 1;
4d2eaafb 234 int server_updates_ptr = 1;
478028e7 235 struct buffer *bp = (struct buffer *)0;
a396d25f 236 int ignorep = 0, client_ignorep = 0;
98bd7ca0
DH
237 int rev_name_len;
238 int i;
b992d7e2 239
5fac73d6
TL
240 if (ddns_update_style != 2)
241 return 0;
242
98bd7ca0
DH
243 if (lease != NULL) {
244 scope = &(lease->scope);
245 addr = lease->ip_addr;
246 } else if (lease6 != NULL) {
247 scope = &(lease6->scope);
248 memcpy(addr.iabuf, lease6->addr.s6_addr, 16);
249 addr.len = 16;
6705543f 250 } else {
98bd7ca0 251 log_fatal("Impossible condition at %s:%d.", MDL);
6705543f
DH
252 /* Silence compiler warnings. */
253 return 0;
254 }
b992d7e2 255
57868747 256 memset(&d1, 0, sizeof(d1));
b992d7e2
DN
257 memset (&ddns_hostname, 0, sizeof (ddns_hostname));
258 memset (&ddns_domainname, 0, sizeof (ddns_domainname));
478028e7 259 memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
b992d7e2
DN
260 memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
261 memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
b992d7e2
DN
262 memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
263
478028e7
TL
264 /* If we are allowed to accept the client's update of its own A
265 record, see if the client wants to update its own A record. */
98bd7ca0 266 if (!(oc = lookup_option(&server_universe, options,
a396d25f
DH
267 SV_CLIENT_UPDATES)) ||
268 evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
98bd7ca0
DH
269 packet->options, options, scope,
270 oc, MDL)) {
478028e7
TL
271 /* If there's no fqdn.no-client-update or if it's
272 nonzero, don't try to use the client-supplied
273 XXX */
ee83afae 274 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
98311e4b 275 FQDN_SERVER_UPDATE)) ||
98bd7ca0
DH
276 evaluate_boolean_option_cache(&ignorep, packet, lease,
277 NULL, packet->options,
278 options, scope, oc, MDL))
478028e7 279 goto noclient;
e9d45f83
TL
280 /* Win98 and Win2k will happily claim to be willing to
281 update an unqualified domain name. */
282 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
283 FQDN_DOMAINNAME)))
284 goto noclient;
ee83afae 285 if (!(oc = lookup_option (&fqdn_universe, packet -> options,
478028e7 286 FQDN_FQDN)) ||
98bd7ca0
DH
287 !evaluate_option_cache(&ddns_fwd_name, packet, lease,
288 NULL, packet->options,
289 options, scope, oc, MDL))
478028e7
TL
290 goto noclient;
291 server_updates_a = 0;
292 goto client_updates;
b992d7e2 293 }
478028e7 294 noclient:
98311e4b
DH
295 /* If do-forward-updates is disabled, this basically means don't
296 do an update unless the client is participating, so if we get
297 here and do-forward-updates is disabled, we can stop. */
98bd7ca0 298 if ((oc = lookup_option (&server_universe, options,
98311e4b 299 SV_DO_FORWARD_UPDATES)) &&
98bd7ca0
DH
300 !evaluate_boolean_option_cache(&ignorep, packet, lease,
301 NULL, packet->options,
302 options, scope, oc, MDL)) {
98311e4b
DH
303 return 0;
304 }
b992d7e2 305
ec64b462
TL
306 /* If it's a static lease, then don't do the DNS update unless we're
307 specifically configured to do so. If the client asked to do its
308 own update and we allowed that, we don't do this test. */
98bd7ca0
DH
309 /* XXX: note that we cannot detect static DHCPv6 leases. */
310 if ((lease != NULL) && (lease->flags & STATIC_LEASE)) {
311 if (!(oc = lookup_option(&server_universe, options,
312 SV_UPDATE_STATIC_LEASES)) ||
313 !evaluate_boolean_option_cache(&ignorep, packet, lease,
314 NULL, packet->options,
315 options, scope, oc, MDL))
ec64b462
TL
316 return 0;
317 }
318
b992d7e2 319 /*
478028e7 320 * Compute the name for the A record.
b992d7e2 321 */
98bd7ca0 322 oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
b992d7e2 323 if (oc)
98bd7ca0
DH
324 s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
325 NULL, packet->options,
326 options, scope, oc, MDL);
98311e4b
DH
327 else
328 s1 = 0;
b992d7e2 329
98bd7ca0 330 oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
b992d7e2 331 if (oc)
98bd7ca0
DH
332 s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
333 NULL, packet->options,
334 options, scope, oc, MDL);
98311e4b
DH
335 else
336 s2 = 0;
b992d7e2
DN
337
338 if (s1 && s2) {
98311e4b
DH
339 if (ddns_hostname.len + ddns_domainname.len > 253) {
340 log_error ("ddns_update: host.domain name too long");
341
342 goto out;
343 }
344
b992d7e2
DN
345 buffer_allocate (&ddns_fwd_name.buffer,
346 ddns_hostname.len + ddns_domainname.len + 2,
347 MDL);
348 if (ddns_fwd_name.buffer) {
349 ddns_fwd_name.data = ddns_fwd_name.buffer -> data;
350 data_string_append (&ddns_fwd_name, &ddns_hostname);
351 ddns_fwd_name.buffer -> data [ddns_fwd_name.len] = '.';
352 ddns_fwd_name.len++;
353 data_string_append (&ddns_fwd_name, &ddns_domainname);
354 ddns_fwd_name.buffer -> data [ddns_fwd_name.len] ='\0';
355 ddns_fwd_name.terminated = 1;
356 }
357 }
478028e7
TL
358 client_updates:
359
360 /* See if there's a name already stored on the lease. */
98bd7ca0 361 if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) {
478028e7
TL
362 /* If there is, see if it's different. */
363 if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
364 memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
365 old_ddns_fwd_name.len)) {
366 /* If the name is different, try to delete
367 the old A record. */
98bd7ca0 368 if (!ddns_removals(lease, lease6))
478028e7
TL
369 goto out;
370 /* If the delete succeeded, go install the new
371 record. */
372 goto in;
373 }
374
98bd7ca0
DH
375 /* See if there's a DHCID on the lease, and if not
376 * then potentially look for 'on events' for ad-hoc ddns.
377 */
378 if (!find_bound_string(&ddns_dhcid, *scope, "ddns-txt") &&
379 (old != NULL)) {
478028e7
TL
380 /* If there's no DHCID, the update was probably
381 done with the old-style ad-hoc DDNS updates.
382 So if the expiry and release events look like
383 they're the same, run them. This should delete
384 the old DDNS data. */
ec64b462 385 if (old -> on_expiry == old -> on_release) {
98bd7ca0
DH
386 execute_statements(NULL, NULL, lease, NULL,
387 NULL, NULL, scope,
388 old->on_expiry);
ec64b462 389 if (old -> on_expiry)
478028e7 390 executable_statement_dereference
ec64b462
TL
391 (&old -> on_expiry, MDL);
392 if (old -> on_release)
478028e7 393 executable_statement_dereference
ec64b462 394 (&old -> on_release, MDL);
478028e7
TL
395 /* Now, install the DDNS data the new way. */
396 goto in;
397 }
75ab3070
DH
398 } else
399 data_string_forget(&ddns_dhcid, MDL);
478028e7 400
ebd0310b
TL
401 /* See if the administrator wants to do updates even
402 in cases where the update already appears to have been
403 done. */
98bd7ca0
DH
404 if (!(oc = lookup_option(&server_universe, options,
405 SV_UPDATE_OPTIMIZATION)) ||
406 evaluate_boolean_option_cache(&ignorep, packet, lease,
407 NULL, packet->options,
408 options, scope, oc, MDL)) {
ebd0310b
TL
409 result = 1;
410 goto noerror;
411 }
75ab3070
DH
412 /* If there's no "ddns-fwd-name" on the lease record, see if
413 * there's a ddns-client-fqdn indicating a previous client
414 * update (if it changes, we need to adjust the PTR).
415 */
98bd7ca0 416 } else if (find_bound_string(&old_ddns_fwd_name, *scope,
75ab3070 417 "ddns-client-fqdn")) {
98311e4b
DH
418 /* If the name is not different, no need to update
419 the PTR record. */
ee83afae
TL
420 if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
421 !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
98311e4b 422 old_ddns_fwd_name.len) &&
98bd7ca0
DH
423 (!(oc = lookup_option(&server_universe, options,
424 SV_UPDATE_OPTIMIZATION)) ||
425 evaluate_boolean_option_cache(&ignorep, packet, lease,
426 NULL, packet->options,
427 options, scope, oc, MDL))) {
478028e7
TL
428 goto noerror;
429 }
430 }
431 in:
432
d758ad8c
TL
433 /* If we don't have a name that the client has been assigned, we
434 can just skip all this. */
435 if (!ddns_fwd_name.len)
436 goto out;
437
98311e4b
DH
438 if (ddns_fwd_name.len > 255) {
439 log_error ("client provided fqdn: too long");
440 goto out;
441 }
442
b992d7e2 443 /*
478028e7
TL
444 * Compute the RR TTL.
445 */
446 ddns_ttl = DEFAULT_DDNS_TTL;
98bd7ca0
DH
447 if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) {
448 if (evaluate_option_cache(&d1, packet, lease, NULL,
449 packet->options, options, scope,
450 oc, MDL)) {
478028e7
TL
451 if (d1.len == sizeof (u_int32_t))
452 ddns_ttl = getULong (d1.data);
453 data_string_forget (&d1, MDL);
454 }
455 }
456
4d2eaafb
DH
457 /* CC: see if we are configured NOT to do reverse ptr updates
458 */
98bd7ca0
DH
459 if ((oc = lookup_option(&server_universe, options,
460 SV_DO_REVERSE_UPDATES)) &&
461 !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
462 packet->options, options,
463 scope, oc, MDL)) {
4d2eaafb
DH
464 server_updates_ptr = 0;
465 }
478028e7
TL
466
467 /*
468 * Compute the reverse IP name.
b992d7e2 469 */
98bd7ca0
DH
470
471 /*
472 * Figure out the length of the part of the name that depends
473 * on the address.
474 */
475 if (addr.len == 4) {
476 char buf[1];
477 /* XXX: WOW this is gross. */
478 rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
479 addr.iabuf[3] & 0xff,
480 addr.iabuf[2] & 0xff,
481 addr.iabuf[1] & 0xff,
482 addr.iabuf[0] & 0xff);
483 } else if (addr.len == 16) {
484 /*
485 * IPv6 reverse names are always the same length, with
486 * 32 hex characters separated by dots.
487 */
488 rev_name_len = 64;
6705543f 489 } else {
98bd7ca0 490 log_fatal("invalid address length %d", addr.len);
6705543f
DH
491 /* Silence compiler warnings. */
492 return 0;
493 }
98bd7ca0
DH
494
495 oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
b992d7e2 496 if (oc)
98bd7ca0
DH
497 s1 = evaluate_option_cache(&d1, packet, lease, NULL,
498 packet->options, options,
499 scope, oc, MDL);
98311e4b
DH
500 else
501 s1 = 0;
502
98bd7ca0 503 if (s1 && ((d1.len + rev_name_len) > 255)) {
98311e4b
DH
504 log_error ("ddns_update: Calculated rev domain name too long.");
505 s1 = 0;
506 data_string_forget (&d1, MDL);
507 }
508
b992d7e2 509 if (oc && s1) {
b992d7e2 510 buffer_allocate (&ddns_rev_name.buffer,
98bd7ca0 511 d1.len + rev_name_len + 1, MDL);
b992d7e2 512 if (ddns_rev_name.buffer) {
98bd7ca0
DH
513 ddns_rev_name.data = ddns_rev_name.buffer->data;
514
515 if (addr.len == 4) {
516 sprintf((char *)ddns_rev_name.buffer->data,
517 "%u.%u.%u.%u.",
518 addr.iabuf[3] & 0xff,
519 addr.iabuf[2] & 0xff,
520 addr.iabuf[1] & 0xff,
521 addr.iabuf[0] & 0xff);
522 } else if (addr.len == 16) {
523 char *p = (char *)&ddns_rev_name.buffer->data;
524 unsigned char *a = addr.iabuf + 15;
525 for (i=0; i<16; i++) {
526 sprintf(p, "%x.%x.",
527 (*a & 0xF), ((*a >> 4) & 0xF));
528 p += 4;
529 a -= 1;
530 }
531 }
98311e4b 532
478028e7
TL
533 ddns_rev_name.len =
534 strlen ((const char *)ddns_rev_name.data);
b992d7e2
DN
535 data_string_append (&ddns_rev_name, &d1);
536 ddns_rev_name.buffer -> data [ddns_rev_name.len] ='\0';
537 ddns_rev_name.terminated = 1;
538 }
98bd7ca0 539
b992d7e2
DN
540 data_string_forget (&d1, MDL);
541 }
542
b992d7e2 543 /*
478028e7 544 * If we are updating the A record, compute the DHCID value.
b992d7e2 545 */
478028e7 546 if (server_updates_a) {
98bd7ca0
DH
547 memset (&ddns_dhcid, 0, sizeof ddns_dhcid);
548 if (lease6 != NULL)
549 result = get_dhcid(&ddns_dhcid, 2,
550 lease6->ia_na->iaid_duid.data,
551 lease6->ia_na->iaid_duid.len);
552 else if ((lease != NULL) && (lease->uid != NULL) &&
553 (lease->uid_len != 0))
d758ad8c
TL
554 result = get_dhcid (&ddns_dhcid,
555 DHO_DHCP_CLIENT_IDENTIFIER,
556 lease -> uid, lease -> uid_len);
98bd7ca0 557 else if (lease != NULL)
d758ad8c
TL
558 result = get_dhcid (&ddns_dhcid, 0,
559 lease -> hardware_addr.hbuf,
560 lease -> hardware_addr.hlen);
98bd7ca0
DH
561 else
562 log_fatal("Impossible condition at %s:%d.", MDL);
563
d758ad8c
TL
564 if (!result)
565 goto badfqdn;
478028e7 566 }
b992d7e2 567
b992d7e2
DN
568 /*
569 * Start the resolver, if necessary.
570 */
571 if (!resolver_inited) {
572 minires_ninit (&resolver_state);
573 resolver_inited = 1;
ec64b462
TL
574 resolver_state.retrans = 1;
575 resolver_state.retry = 1;
b992d7e2
DN
576 }
577
b992d7e2
DN
578 /*
579 * Perform updates.
580 */
3004bebf
DH
581 if (ddns_fwd_name.len && ddns_dhcid.len) {
582 unsigned conflict;
583
98bd7ca0 584 oc = lookup_option(&server_universe, options,
3004bebf
DH
585 SV_DDNS_CONFLICT_DETECT);
586 if (!oc ||
587 evaluate_boolean_option_cache(&ignorep, packet, lease,
588 NULL, packet->options,
98bd7ca0 589 options, scope, oc, MDL))
3004bebf
DH
590 conflict = 1;
591 else
592 conflict = 0;
593
98bd7ca0
DH
594 rcode1 = ddns_update_fwd(&ddns_fwd_name, addr, &ddns_dhcid,
595 ddns_ttl, 0, conflict);
3004bebf 596 }
75ab3070 597
4d2eaafb 598 if (rcode1 == ISC_R_SUCCESS && server_updates_ptr) {
478028e7 599 if (ddns_fwd_name.len && ddns_rev_name.len)
bdad826f
TL
600 rcode2 = ddns_update_ptr (&ddns_fwd_name,
601 &ddns_rev_name, ddns_ttl);
602 } else
ec64b462 603 rcode2 = rcode1;
b992d7e2 604
ec64b462
TL
605 if (rcode1 == ISC_R_SUCCESS &&
606 (server_updates_a || rcode2 == ISC_R_SUCCESS)) {
98bd7ca0
DH
607 bind_ds_value(scope, server_updates_a ? "ddns-fwd-name"
608 : "ddns-client-fqdn",
609 &ddns_fwd_name);
ee83afae 610 if (server_updates_a)
98bd7ca0 611 bind_ds_value(scope, "ddns-txt", &ddns_dhcid);
b992d7e2 612 }
bdad826f 613
4d2eaafb 614 if (rcode2 == ISC_R_SUCCESS && server_updates_ptr) {
98bd7ca0 615 bind_ds_value(scope, "ddns-rev-name", &ddns_rev_name);
b992d7e2
DN
616 }
617
478028e7 618 noerror:
1ba87b37
EH
619 /*
620 * If fqdn-reply option is disabled in dhcpd.conf, then don't
621 * send the client an FQDN option at all, even if one was requested.
622 * (WinXP clients allegedly misbehave if the option is present,
623 * refusing to handle PTR updates themselves).
624 */
98bd7ca0
DH
625 if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) &&
626 !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
627 packet->options, options,
628 scope, oc, MDL)) {
1ba87b37
EH
629 goto badfqdn;
630
a396d25f
DH
631 /* If we're ignoring client updates, then we tell a sort of 'white
632 * lie'. We've already updated the name the server wants (per the
633 * config written by the server admin). Now let the client do as
634 * it pleases with the name they supplied (if any).
635 *
636 * We only form an FQDN option this way if the client supplied an
637 * FQDN option that had FQDN_SERVER_UPDATE set false.
638 */
1ba87b37 639 } else if (client_ignorep &&
a396d25f
DH
640 (oc = lookup_option(&fqdn_universe, packet->options,
641 FQDN_SERVER_UPDATE)) &&
642 !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
98bd7ca0
DH
643 packet->options, options,
644 scope, oc, MDL)) {
a396d25f
DH
645 oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN);
646 if (oc && evaluate_option_cache(&d1, packet, lease, NULL,
98bd7ca0
DH
647 packet->options, options,
648 scope, oc, MDL)) {
a396d25f
DH
649 if (d1.len == 0 ||
650 !buffer_allocate(&bp, d1.len + 5, MDL))
651 goto badfqdn;
652
653 /* Server pretends it is not updating. */
654 bp->data[0] = 0;
98bd7ca0 655 if (!save_option_buffer(&fqdn_universe, options,
a396d25f 656 bp, &bp->data[0], 1,
8f4c32a1 657 FQDN_SERVER_UPDATE, 0))
a396d25f
DH
658 goto badfqdn;
659
660 /* Client is encouraged to update. */
661 bp->data[1] = 0;
98bd7ca0 662 if (!save_option_buffer(&fqdn_universe, options,
a396d25f 663 bp, &bp->data[1], 1,
8f4c32a1 664 FQDN_NO_CLIENT_UPDATE, 0))
a396d25f
DH
665 goto badfqdn;
666
667 /* Use the encoding of client's FQDN option. */
668 oc = lookup_option(&fqdn_universe, packet->options,
669 FQDN_ENCODED);
98bd7ca0
DH
670 if (oc &&
671 evaluate_boolean_option_cache(&ignorep, packet,
672 lease, NULL,
673 packet->options,
674 options, scope,
675 oc, MDL))
a396d25f
DH
676 bp->data[2] = 1; /* FQDN is encoded. */
677 else
678 bp->data[2] = 0; /* FQDN is not encoded. */
679
98bd7ca0 680 if (!save_option_buffer(&fqdn_universe, options,
a396d25f 681 bp, &bp->data[2], 1,
8f4c32a1 682 FQDN_ENCODED, 0))
a396d25f
DH
683 goto badfqdn;
684
685 /* Current FQDN drafts indicate 255 is mandatory. */
686 bp->data[3] = 255;
98bd7ca0 687 if (!save_option_buffer(&fqdn_universe, options,
a396d25f 688 bp, &bp->data[3], 1,
8f4c32a1 689 FQDN_RCODE1, 0))
a396d25f
DH
690 goto badfqdn;
691
692 bp->data[4] = 255;
98bd7ca0 693 if (!save_option_buffer(&fqdn_universe, options,
a396d25f 694 bp, &bp->data[4], 1,
8f4c32a1 695 FQDN_RCODE2, 0))
a396d25f
DH
696 goto badfqdn;
697
698 /* Copy in the FQDN supplied by the client. Note well
699 * that the format of this option in the cache is going
700 * to be in text format. If the fqdn supplied by the
701 * client is encoded, it is decoded into the option
702 * cache when parsed out of the packet. It will be
703 * re-encoded when the option is assembled to be
704 * transmitted if the client elects that encoding.
705 */
706 memcpy(&bp->data[5], d1.data, d1.len);
98bd7ca0 707 if (!save_option_buffer(&fqdn_universe, options,
a396d25f 708 bp, &bp->data[5], 1,
8f4c32a1 709 FQDN_FQDN, 0))
a396d25f
DH
710 goto badfqdn;
711
712 data_string_forget(&d1, MDL);
713 }
714 /* Set up the outgoing FQDN option if there was an incoming
715 * FQDN option. If there's a valid FQDN option, there MUST
716 * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed
717 * length head of the option contents, so we test the latter
718 * to detect the presence of the former.
719 */
720 } else if ((oc = lookup_option(&fqdn_universe, packet->options,
721 FQDN_ENCODED)) &&
722 buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
478028e7 723 bp -> data [0] = server_updates_a;
98bd7ca0
DH
724 if (!save_option_buffer(&fqdn_universe, options,
725 bp, &bp->data [0], 1,
726 FQDN_SERVER_UPDATE, 0))
478028e7
TL
727 goto badfqdn;
728 bp -> data [1] = server_updates_a;
98bd7ca0
DH
729 if (!save_option_buffer(&fqdn_universe, options,
730 bp, &bp->data [1], 1,
f7fdb216 731 FQDN_NO_CLIENT_UPDATE, 0))
478028e7 732 goto badfqdn;
a396d25f 733
478028e7 734 /* Do the same encoding the client did. */
a396d25f
DH
735 if (evaluate_boolean_option_cache(&ignorep, packet, lease,
736 NULL, packet->options,
98bd7ca0 737 options, scope, oc, MDL))
478028e7
TL
738 bp -> data [2] = 1;
739 else
740 bp -> data [2] = 0;
98bd7ca0
DH
741 if (!save_option_buffer(&fqdn_universe, options,
742 bp, &bp->data [2], 1,
743 FQDN_ENCODED, 0))
478028e7 744 goto badfqdn;
ec64b462 745 bp -> data [3] = isc_rcode_to_ns (rcode1);
98bd7ca0
DH
746 if (!save_option_buffer(&fqdn_universe, options,
747 bp, &bp->data [3], 1,
748 FQDN_RCODE1, 0))
478028e7 749 goto badfqdn;
ec64b462 750 bp -> data [4] = isc_rcode_to_ns (rcode2);
98bd7ca0
DH
751 if (!save_option_buffer(&fqdn_universe, options,
752 bp, &bp->data [4], 1,
753 FQDN_RCODE2, 0))
478028e7
TL
754 goto badfqdn;
755 if (ddns_fwd_name.len) {
756 memcpy (&bp -> data [5],
757 ddns_fwd_name.data, ddns_fwd_name.len);
98bd7ca0
DH
758 if (!save_option_buffer(&fqdn_universe, options,
759 bp, &bp->data [5],
478028e7 760 ddns_fwd_name.len,
f7fdb216 761 FQDN_FQDN, 0))
478028e7
TL
762 goto badfqdn;
763 }
b992d7e2
DN
764 }
765
478028e7
TL
766 badfqdn:
767 out:
b992d7e2
DN
768 /*
769 * Final cleanup.
770 */
a396d25f
DH
771 data_string_forget(&d1, MDL);
772 data_string_forget(&ddns_hostname, MDL);
773 data_string_forget(&ddns_domainname, MDL);
774 data_string_forget(&old_ddns_fwd_name, MDL);
775 data_string_forget(&ddns_fwd_name, MDL);
776 data_string_forget(&ddns_rev_name, MDL);
777 data_string_forget(&ddns_dhcid, MDL);
c3993513 778 if (bp)
a396d25f 779 buffer_dereference(&bp, MDL);
b992d7e2 780
478028e7 781 return result;
b992d7e2
DN
782}
783
98bd7ca0
DH
784/* Remove relevant entries from DNS. */
785int
786ddns_removals(struct lease *lease, struct iaaddr *lease6)
385fcb27 787{
b992d7e2
DN
788 struct data_string ddns_fwd_name;
789 struct data_string ddns_rev_name;
b992d7e2 790 struct data_string ddns_dhcid;
ec64b462 791 isc_result_t rcode;
98bd7ca0
DH
792 struct binding_scope **scope;
793 struct iaddr addr;
478028e7 794 int result = 0;
ee83afae 795 int client_updated = 0;
b992d7e2 796
98bd7ca0
DH
797 if (lease != NULL) {
798 scope = &(lease->scope);
799 addr = lease->ip_addr;
800 } else if (lease6 != NULL) {
801 scope = &(lease6->scope);
802 memcpy(addr.iabuf, lease6->addr.s6_addr, 16);
803 addr.len = 16;
804 } else
805 return 0;
806
b992d7e2 807 /* No scope implies that DDNS has not been performed for this lease. */
98bd7ca0 808 if (*scope == NULL)
478028e7 809 return 0;
b992d7e2 810
98311e4b
DH
811 if (ddns_update_style != 2)
812 return 0;
813
b992d7e2
DN
814 /*
815 * Look up stored names.
816 */
817 memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
818 memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
b992d7e2
DN
819 memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
820
b992d7e2
DN
821 /*
822 * Start the resolver, if necessary.
823 */
824 if (!resolver_inited) {
825 minires_ninit (&resolver_state);
826 resolver_inited = 1;
98311e4b
DH
827 resolver_state.retrans = 1;
828 resolver_state.retry = 1;
b992d7e2
DN
829 }
830
478028e7
TL
831 /* We need the fwd name whether we are deleting both records or just
832 the PTR record, so if it's not there, we can't proceed. */
98bd7ca0 833 if (!find_bound_string(&ddns_fwd_name, *scope, "ddns-fwd-name")) {
ee83afae
TL
834 /* If there's no ddns-fwd-name, look for the client fqdn,
835 in case the client did the update. */
98bd7ca0
DH
836 if (find_bound_string(&ddns_fwd_name, *scope,
837 "ddns-client-fqdn"))
838 client_updated = 1;
ee83afae
TL
839 goto try_rev;
840 }
478028e7
TL
841
842 /* If the ddns-txt binding isn't there, this isn't an interim
843 or rfc3??? record, so we can't delete the A record using
844 this mechanism, but we can delete the PTR record. */
98bd7ca0 845 if (!find_bound_string (&ddns_dhcid, *scope, "ddns-txt")) {
478028e7
TL
846 result = 1;
847 goto try_rev;
848 }
849
b992d7e2
DN
850 /*
851 * Perform removals.
852 */
d758ad8c 853 if (ddns_fwd_name.len)
98bd7ca0 854 rcode = ddns_remove_fwd(&ddns_fwd_name, addr, &ddns_dhcid);
d758ad8c
TL
855 else
856 rcode = ISC_R_SUCCESS;
478028e7 857
ec64b462 858 if (rcode == ISC_R_SUCCESS) {
478028e7 859 result = 1;
98bd7ca0
DH
860 unset(*scope, "ddns-fwd-name");
861 unset(*scope, "ddns-txt");
478028e7 862 try_rev:
98bd7ca0
DH
863 if (find_bound_string(&ddns_rev_name, *scope,
864 "ddns-rev-name")) {
ee83afae 865 if (ddns_remove_ptr(&ddns_rev_name) == NOERROR) {
98bd7ca0 866 unset(*scope, "ddns-rev-name");
ee83afae 867 if (client_updated)
98bd7ca0 868 unset(*scope, "ddns-client-fqdn");
d758ad8c
TL
869 /* XXX this is to compensate for a bug in
870 XXX 3.0rc8, and should be removed before
871 XXX 3.0pl1. */
872 else if (!ddns_fwd_name.len)
98bd7ca0 873 unset(*scope, "ddns-text");
ee83afae 874 } else
478028e7
TL
875 result = 0;
876 }
b992d7e2
DN
877 }
878
b992d7e2
DN
879 data_string_forget (&ddns_fwd_name, MDL);
880 data_string_forget (&ddns_rev_name, MDL);
b992d7e2
DN
881 data_string_forget (&ddns_dhcid, MDL);
882
478028e7 883 return result;
b992d7e2
DN
884}
885
b992d7e2 886#endif /* NSUPDATE */