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