Annotation of embedaddon/dhcp/common/dns.c, revision 1.1
1.1 ! misho 1: /* dns.c
! 2:
! 3: Domain Name Service subroutines. */
! 4:
! 5: /*
! 6: * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
! 7: * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
! 8: * Copyright (c) 2001-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 written for Internet Systems Consortium
! 29: * by Ted Lemon in cooperation with Nominum, Inc.
! 30: * To learn more about Internet Systems Consortium, see
! 31: * ``https://www.isc.org/''. To learn more about Nominum, Inc., see
! 32: * ``http://www.nominum.com''.
! 33: */
! 34:
! 35: #include "dhcpd.h"
! 36: #include "arpa/nameser.h"
! 37: #include "dst/md5.h"
! 38:
! 39: /* This file is kind of a crutch for the BIND 8 nsupdate code, which has
! 40: * itself been cruelly hacked from its original state. What this code
! 41: * does is twofold: first, it maintains a database of zone cuts that can
! 42: * be used to figure out which server should be contacted to update any
! 43: * given domain name. Secondly, it maintains a set of named TSIG keys,
! 44: * and associates those keys with zones. When an update is requested for
! 45: * a particular zone, the key associated with that zone is used for the
! 46: * update.
! 47: *
! 48: * The way this works is that you define the domain name to which an
! 49: * SOA corresponds, and the addresses of some primaries for that domain name:
! 50: *
! 51: * zone FOO.COM {
! 52: * primary 10.0.17.1;
! 53: * secondary 10.0.22.1, 10.0.23.1;
! 54: * key "FOO.COM Key";
! 55: * }
! 56: *
! 57: * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
! 58: * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
! 59: * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
! 60: * looks for "FOO.COM", finds it. So it
! 61: * attempts the update to the primary for FOO.COM. If that times out, it
! 62: * tries the secondaries. You can list multiple primaries if you have some
! 63: * kind of magic name server that supports that. You shouldn't list
! 64: * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
! 65: * support update forwarding, AFAIK). If no TSIG key is listed, the update
! 66: * is attempted without TSIG.
! 67: *
! 68: * The DHCP server tries to find an existing zone for any given name by
! 69: * trying to look up a local zone structure for each domain containing
! 70: * that name, all the way up to '.'. If it finds one cached, it tries
! 71: * to use that one to do the update. That's why it tries to update
! 72: * "FOO.COM" above, even though theoretically it should try GAZANGA...
! 73: * and TOPANGA... first.
! 74: *
! 75: * If the update fails with a predefined or cached zone (we'll get to
! 76: * those in a second), then it tries to find a more specific zone. This
! 77: * is done by looking first for an SOA for GAZANGA.TOPANGA.FOO.COM. Then
! 78: * an SOA for TOPANGA.FOO.COM is sought. If during this search a predefined
! 79: * or cached zone is found, the update fails - there's something wrong
! 80: * somewhere.
! 81: *
! 82: * If a more specific zone _is_ found, that zone is cached for the length of
! 83: * its TTL in the same database as that described above. TSIG updates are
! 84: * never done for cached zones - if you want TSIG updates you _must_
! 85: * write a zone definition linking the key to the zone. In cases where you
! 86: * know for sure what the key is but do not want to hardcode the IP addresses
! 87: * of the primary or secondaries, a zone declaration can be made that doesn't
! 88: * include any primary or secondary declarations. When the DHCP server
! 89: * encounters this while hunting up a matching zone for a name, it looks up
! 90: * the SOA, fills in the IP addresses, and uses that record for the update.
! 91: * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
! 92: * discarded, TSIG key and all. The search for the zone then continues as if
! 93: * the zone record hadn't been found. Zones without IP addresses don't
! 94: * match when initially hunting for a predefined or cached zone to update.
! 95: *
! 96: * When an update is attempted and no predefined or cached zone is found
! 97: * that matches any enclosing domain of the domain being updated, the DHCP
! 98: * server goes through the same process that is done when the update to a
! 99: * predefined or cached zone fails - starting with the most specific domain
! 100: * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
! 101: * it tries to look up an SOA record. When it finds one, it creates a cached
! 102: * zone and attempts an update, and gives up if the update fails.
! 103: *
! 104: * TSIG keys are defined like this:
! 105: *
! 106: * key "FOO.COM Key" {
! 107: * algorithm HMAC-MD5.SIG-ALG.REG.INT;
! 108: * secret <Base64>;
! 109: * }
! 110: *
! 111: * <Base64> is a number expressed in base64 that represents the key.
! 112: * It's also permissible to use a quoted string here - this will be
! 113: * translated as the ASCII bytes making up the string, and will not
! 114: * include any NUL termination. The key name can be any text string,
! 115: * and the key type must be one of the key types defined in the draft
! 116: * or by the IANA. Currently only the HMAC-MD5... key type is
! 117: * supported.
! 118: */
! 119:
! 120: dns_zone_hash_t *dns_zone_hash;
! 121:
! 122: #if defined (NSUPDATE)
! 123: isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
! 124: struct dns_zone *zone)
! 125: {
! 126: ns_tsig_key *tkey;
! 127:
! 128: if (!zone)
! 129: return ISC_R_NOTFOUND;
! 130:
! 131: if (!zone -> key) {
! 132: return ISC_R_KEY_UNKNOWN;
! 133: }
! 134:
! 135: if ((!zone -> key -> name ||
! 136: strlen (zone -> key -> name) > NS_MAXDNAME) ||
! 137: (!zone -> key -> algorithm ||
! 138: strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
! 139: (!zone -> key) ||
! 140: (!zone -> key -> key) ||
! 141: (zone -> key -> key -> len == 0)) {
! 142: return ISC_R_INVALIDKEY;
! 143: }
! 144: tkey = dmalloc (sizeof *tkey, MDL);
! 145: if (!tkey) {
! 146: nomem:
! 147: return ISC_R_NOMEMORY;
! 148: }
! 149: memset (tkey, 0, sizeof *tkey);
! 150: tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
! 151: if (!tkey -> data) {
! 152: dfree (tkey, MDL);
! 153: goto nomem;
! 154: }
! 155: strcpy (tkey -> name, zone -> key -> name);
! 156: strcpy (tkey -> alg, zone -> key -> algorithm);
! 157: memcpy (tkey -> data,
! 158: zone -> key -> key -> value, zone -> key -> key -> len);
! 159: tkey -> len = zone -> key -> key -> len;
! 160: *key = tkey;
! 161: return ISC_R_SUCCESS;
! 162: }
! 163:
! 164: void tkey_free (ns_tsig_key **key)
! 165: {
! 166: if ((*key) -> data)
! 167: dfree ((*key) -> data, MDL);
! 168: dfree ((*key), MDL);
! 169: *key = (ns_tsig_key *)0;
! 170: }
! 171: #endif
! 172:
! 173: isc_result_t enter_dns_zone (struct dns_zone *zone)
! 174: {
! 175: struct dns_zone *tz = (struct dns_zone *)0;
! 176:
! 177: if (dns_zone_hash) {
! 178: dns_zone_hash_lookup (&tz,
! 179: dns_zone_hash, zone -> name, 0, MDL);
! 180: if (tz == zone) {
! 181: dns_zone_dereference (&tz, MDL);
! 182: return ISC_R_SUCCESS;
! 183: }
! 184: if (tz) {
! 185: dns_zone_hash_delete (dns_zone_hash,
! 186: zone -> name, 0, MDL);
! 187: dns_zone_dereference (&tz, MDL);
! 188: }
! 189: } else {
! 190: if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
! 191: return ISC_R_NOMEMORY;
! 192: }
! 193: dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
! 194: return ISC_R_SUCCESS;
! 195: }
! 196:
! 197: isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
! 198: {
! 199: int len;
! 200: char *tname = (char *)0;
! 201: isc_result_t status;
! 202:
! 203: if (!dns_zone_hash)
! 204: return ISC_R_NOTFOUND;
! 205:
! 206: len = strlen (name);
! 207: if (name [len - 1] != '.') {
! 208: tname = dmalloc ((unsigned)len + 2, MDL);
! 209: if (!tname)
! 210: return ISC_R_NOMEMORY;
! 211: strcpy (tname, name);
! 212: tname [len] = '.';
! 213: tname [len + 1] = 0;
! 214: name = tname;
! 215: }
! 216: if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
! 217: status = ISC_R_NOTFOUND;
! 218: else
! 219: status = ISC_R_SUCCESS;
! 220:
! 221: if (tname)
! 222: dfree (tname, MDL);
! 223: return status;
! 224: }
! 225:
! 226: int dns_zone_dereference (ptr, file, line)
! 227: struct dns_zone **ptr;
! 228: const char *file;
! 229: int line;
! 230: {
! 231: struct dns_zone *dns_zone;
! 232:
! 233: if (!ptr || !*ptr) {
! 234: log_error ("%s(%d): null pointer", file, line);
! 235: #if defined (POINTER_DEBUG)
! 236: abort ();
! 237: #else
! 238: return 0;
! 239: #endif
! 240: }
! 241:
! 242: dns_zone = *ptr;
! 243: *ptr = (struct dns_zone *)0;
! 244: --dns_zone -> refcnt;
! 245: rc_register (file, line, ptr, dns_zone, dns_zone -> refcnt, 1, RC_MISC);
! 246: if (dns_zone -> refcnt > 0)
! 247: return 1;
! 248:
! 249: if (dns_zone -> refcnt < 0) {
! 250: log_error ("%s(%d): negative refcnt!", file, line);
! 251: #if defined (DEBUG_RC_HISTORY)
! 252: dump_rc_history (dns_zone);
! 253: #endif
! 254: #if defined (POINTER_DEBUG)
! 255: abort ();
! 256: #else
! 257: return 0;
! 258: #endif
! 259: }
! 260:
! 261: if (dns_zone -> name)
! 262: dfree (dns_zone -> name, file, line);
! 263: if (dns_zone -> key)
! 264: omapi_auth_key_dereference (&dns_zone -> key, file, line);
! 265: if (dns_zone -> primary)
! 266: option_cache_dereference (&dns_zone -> primary, file, line);
! 267: if (dns_zone -> secondary)
! 268: option_cache_dereference (&dns_zone -> secondary, file, line);
! 269: dfree (dns_zone, file, line);
! 270: return 1;
! 271: }
! 272:
! 273: #if defined (NSUPDATE)
! 274: isc_result_t find_cached_zone (const char *dname, ns_class class,
! 275: char *zname, size_t zsize,
! 276: struct in_addr *addrs,
! 277: int naddrs, int *naddrout,
! 278: struct dns_zone **zcookie)
! 279: {
! 280: isc_result_t status = ISC_R_NOTFOUND;
! 281: const char *np;
! 282: struct dns_zone *zone = (struct dns_zone *)0;
! 283: struct data_string nsaddrs;
! 284: int ix;
! 285:
! 286: /* The absence of the zcookie pointer indicates that we
! 287: succeeded previously, but the update itself failed, meaning
! 288: that we shouldn't use the cached zone. */
! 289: if (!zcookie)
! 290: return ISC_R_NOTFOUND;
! 291:
! 292: /* We can't look up a null zone. */
! 293: if (!dname || !*dname)
! 294: return ISC_R_INVALIDARG;
! 295:
! 296: /*
! 297: * For each subzone, try to find a cached zone.
! 298: */
! 299: for (np = dname;;) {
! 300: status = dns_zone_lookup (&zone, np);
! 301: if (status == ISC_R_SUCCESS)
! 302: break;
! 303:
! 304: np = strchr(np, '.');
! 305: if (np == NULL)
! 306: break;
! 307: np++;
! 308: }
! 309:
! 310: if (status != ISC_R_SUCCESS)
! 311: return status;
! 312:
! 313: /* Make sure the zone is valid. */
! 314: if (zone -> timeout && zone -> timeout < cur_time) {
! 315: dns_zone_dereference (&zone, MDL);
! 316: return ISC_R_CANCELED;
! 317: }
! 318:
! 319: /* Make sure the zone name will fit. */
! 320: if (strlen (zone -> name) > zsize) {
! 321: dns_zone_dereference (&zone, MDL);
! 322: return ISC_R_NOSPACE;
! 323: }
! 324: strcpy (zname, zone -> name);
! 325:
! 326: memset (&nsaddrs, 0, sizeof nsaddrs);
! 327: ix = 0;
! 328:
! 329: if (zone -> primary) {
! 330: if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
! 331: (struct lease *)0,
! 332: (struct client_state *)0,
! 333: (struct option_state *)0,
! 334: (struct option_state *)0,
! 335: &global_scope,
! 336: zone -> primary, MDL)) {
! 337: int ip = 0;
! 338: while (ix < naddrs) {
! 339: if (ip + 4 > nsaddrs.len)
! 340: break;
! 341: memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
! 342: ip += 4;
! 343: ix++;
! 344: }
! 345: data_string_forget (&nsaddrs, MDL);
! 346: }
! 347: }
! 348: if (zone -> secondary) {
! 349: if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
! 350: (struct lease *)0,
! 351: (struct client_state *)0,
! 352: (struct option_state *)0,
! 353: (struct option_state *)0,
! 354: &global_scope,
! 355: zone -> secondary, MDL)) {
! 356: int ip = 0;
! 357: while (ix < naddrs) {
! 358: if (ip + 4 > nsaddrs.len)
! 359: break;
! 360: memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
! 361: ip += 4;
! 362: ix++;
! 363: }
! 364: data_string_forget (&nsaddrs, MDL);
! 365: }
! 366: }
! 367:
! 368: /* It's not an error for zcookie to have a value here - actually,
! 369: it's quite likely, because res_nupdate cycles through all the
! 370: names in the update looking for their zones. */
! 371: if (!*zcookie)
! 372: dns_zone_reference (zcookie, zone, MDL);
! 373: dns_zone_dereference (&zone, MDL);
! 374: if (naddrout)
! 375: *naddrout = ix;
! 376: return ISC_R_SUCCESS;
! 377: }
! 378:
! 379: void forget_zone (struct dns_zone **zone)
! 380: {
! 381: dns_zone_dereference (zone, MDL);
! 382: }
! 383:
! 384: void repudiate_zone (struct dns_zone **zone)
! 385: {
! 386: /* XXX Currently we're not differentiating between a cached
! 387: XXX zone and a zone that's been repudiated, which means
! 388: XXX that if we reap cached zones, we blow away repudiated
! 389: XXX zones. This isn't a big problem since we're not yet
! 390: XXX caching zones... :'} */
! 391:
! 392: (*zone) -> timeout = cur_time - 1;
! 393: dns_zone_dereference (zone, MDL);
! 394: }
! 395:
! 396: void cache_found_zone (ns_class class,
! 397: char *zname, struct in_addr *addrs, int naddrs)
! 398: {
! 399: struct dns_zone *zone = (struct dns_zone *)0;
! 400: int ix = strlen (zname);
! 401:
! 402: if (zname [ix - 1] == '.')
! 403: ix = 0;
! 404:
! 405: /* See if there's already such a zone. */
! 406: if (dns_zone_lookup (&zone, zname) == ISC_R_SUCCESS) {
! 407: /* If it's not a dynamic zone, leave it alone. */
! 408: if (!zone -> timeout)
! 409: return;
! 410: /* Address may have changed, so just blow it away. */
! 411: if (zone -> primary)
! 412: option_cache_dereference (&zone -> primary, MDL);
! 413: if (zone -> secondary)
! 414: option_cache_dereference (&zone -> secondary, MDL);
! 415: } else if (!dns_zone_allocate (&zone, MDL))
! 416: return;
! 417:
! 418: if (!zone -> name) {
! 419: zone -> name =
! 420: dmalloc (strlen (zname) + 1 + (ix != 0), MDL);
! 421: if (!zone -> name) {
! 422: dns_zone_dereference (&zone, MDL);
! 423: return;
! 424: }
! 425: strcpy (zone -> name, zname);
! 426: /* Add a trailing '.' if it was missing. */
! 427: if (ix) {
! 428: zone -> name [ix] = '.';
! 429: zone -> name [ix + 1] = 0;
! 430: }
! 431: }
! 432:
! 433: /* XXX Need to get the lower-level code to push the actual zone
! 434: XXX TTL up to us. */
! 435: zone -> timeout = cur_time + 1800;
! 436:
! 437: if (!option_cache_allocate (&zone -> primary, MDL)) {
! 438: dns_zone_dereference (&zone, MDL);
! 439: return;
! 440: }
! 441: if (!buffer_allocate (&zone -> primary -> data.buffer,
! 442: naddrs * sizeof (struct in_addr), MDL)) {
! 443: dns_zone_dereference (&zone, MDL);
! 444: return;
! 445: }
! 446: memcpy (zone -> primary -> data.buffer -> data,
! 447: addrs, naddrs * sizeof *addrs);
! 448: zone -> primary -> data.data =
! 449: &zone -> primary -> data.buffer -> data [0];
! 450: zone -> primary -> data.len = naddrs * sizeof *addrs;
! 451:
! 452: enter_dns_zone (zone);
! 453: }
! 454:
! 455: /* Have to use TXT records for now. */
! 456: #define T_DHCID T_TXT
! 457:
! 458: int get_dhcid (struct data_string *id,
! 459: int type, const u_int8_t *data, unsigned len)
! 460: {
! 461: unsigned char buf[MD5_DIGEST_LENGTH];
! 462: MD5_CTX md5;
! 463: int i;
! 464:
! 465: /* Types can only be 0..(2^16)-1. */
! 466: if (type < 0 || type > 65535)
! 467: return 0;
! 468:
! 469: /* Hexadecimal MD5 digest plus two byte type and NUL. */
! 470: if (!buffer_allocate (&id -> buffer,
! 471: (MD5_DIGEST_LENGTH * 2) + 3, MDL))
! 472: return 0;
! 473: id -> data = id -> buffer -> data;
! 474:
! 475: /*
! 476: * DHCP clients and servers should use the following forms of client
! 477: * identification, starting with the most preferable, and finishing
! 478: * with the least preferable. If the client does not send any of these
! 479: * forms of identification, the DHCP/DDNS interaction is not defined by
! 480: * this specification. The most preferable form of identification is
! 481: * the Globally Unique Identifier Option [TBD]. Next is the DHCP
! 482: * Client Identifier option. Last is the client's link-layer address,
! 483: * as conveyed in its DHCPREQUEST message. Implementors should note
! 484: * that the link-layer address cannot be used if there are no
! 485: * significant bytes in the chaddr field of the DHCP client's request,
! 486: * because this does not constitute a unique identifier.
! 487: * -- "Interaction between DHCP and DNS"
! 488: * <draft-ietf-dhc-dhcp-dns-12.txt>
! 489: * M. Stapp, Y. Rekhter
! 490: */
! 491:
! 492: /* Put the type in the first two bytes. */
! 493: id->buffer->data[0] = "0123456789abcdef"[(type >> 4) & 0xf];
! 494: /* This should have been [type & 0xf] but now that
! 495: * it is in use we need to leave it this way in order
! 496: * to avoid disturbing customer's lease files
! 497: */
! 498: id -> buffer -> data [1] = "0123456789abcdef" [type % 15];
! 499:
! 500: /* Mash together an MD5 hash of the identifier. */
! 501: MD5_Init (&md5);
! 502: MD5_Update (&md5, data, len);
! 503: MD5_Final (buf, &md5);
! 504:
! 505: /* Convert into ASCII. */
! 506: for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
! 507: id -> buffer -> data [i * 2 + 2] =
! 508: "0123456789abcdef" [(buf [i] >> 4) & 0xf];
! 509: id -> buffer -> data [i * 2 + 3] =
! 510: "0123456789abcdef" [buf [i] & 0xf];
! 511: }
! 512: id -> len = MD5_DIGEST_LENGTH * 2 + 2;
! 513: id -> buffer -> data [id -> len] = 0;
! 514: id -> terminated = 1;
! 515:
! 516: return 1;
! 517: }
! 518:
! 519: /* Now for the DDNS update code that is shared between client and
! 520: server... */
! 521:
! 522: isc_result_t
! 523: ddns_update_fwd(struct data_string *ddns_fwd_name, struct iaddr ddns_addr,
! 524: struct data_string *ddns_dhcid, unsigned long ttl,
! 525: unsigned rrsetp, unsigned conflict) {
! 526: ns_updque updqueue;
! 527: ns_updrec *updrec;
! 528: isc_result_t result;
! 529: const char *logstr;
! 530: char ddns_address[
! 531: sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
! 532: int ddns_address_type;
! 533:
! 534: /*
! 535: * We want to delete either A or AAAA records, depending on
! 536: * whether we have an IPv4 or an IPv6 address.
! 537: */
! 538: if (ddns_addr.len == 4) {
! 539: ddns_address_type = T_A;
! 540: } else if (ddns_addr.len == 16) {
! 541: ddns_address_type = T_AAAA;
! 542: } else {
! 543: return ISC_R_INVALIDARG;
! 544: }
! 545: strcpy(ddns_address, piaddr(ddns_addr));
! 546:
! 547: /*
! 548: * When a DHCP client or server intends to update an A RR, it first
! 549: * prepares a DNS UPDATE query which includes as a prerequisite the
! 550: * assertion that the name does not exist. The update section of the
! 551: * query attempts to add the new name and its IP address mapping (an A
! 552: * RR), and the DHCID RR with its unique client-identity.
! 553: * -- "Interaction between DHCP and DNS"
! 554: */
! 555:
! 556: ISC_LIST_INIT (updqueue);
! 557:
! 558: /*
! 559: * A RR does not exist.
! 560: */
! 561: updrec = minires_mkupdrec (S_PREREQ,
! 562: (const char *)ddns_fwd_name -> data,
! 563: C_IN, ddns_address_type, 0);
! 564: if (!updrec) {
! 565: result = ISC_R_NOMEMORY;
! 566: goto error;
! 567: }
! 568:
! 569: updrec -> r_data = (unsigned char *)0;
! 570: updrec -> r_size = 0;
! 571: updrec -> r_opcode = rrsetp ? NXRRSET : NXDOMAIN;
! 572:
! 573: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 574:
! 575:
! 576: /*
! 577: * Add A RR.
! 578: */
! 579: updrec = minires_mkupdrec (S_UPDATE,
! 580: (const char *)ddns_fwd_name -> data,
! 581: C_IN, ddns_address_type, ttl);
! 582: if (!updrec) {
! 583: result = ISC_R_NOMEMORY;
! 584: goto error;
! 585: }
! 586:
! 587: updrec -> r_data = (unsigned char *)ddns_address;
! 588: updrec -> r_size = strlen (ddns_address);
! 589: updrec -> r_opcode = ADD;
! 590:
! 591: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 592:
! 593:
! 594: /*
! 595: * Add DHCID RR.
! 596: */
! 597: updrec = minires_mkupdrec (S_UPDATE,
! 598: (const char *)ddns_fwd_name -> data,
! 599: C_IN, T_DHCID, ttl);
! 600: if (!updrec) {
! 601: result = ISC_R_NOMEMORY;
! 602: goto error;
! 603: }
! 604:
! 605: updrec -> r_data = ddns_dhcid -> data;
! 606: updrec -> r_size = ddns_dhcid -> len;
! 607: updrec -> r_opcode = ADD;
! 608:
! 609: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 610:
! 611:
! 612: /*
! 613: * Attempt to perform the update.
! 614: */
! 615: result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
! 616:
! 617: #ifdef DEBUG_DNS_UPDATES
! 618: print_dns_status ((int)result, &updqueue);
! 619: #endif
! 620:
! 621: /*
! 622: * If this update operation succeeds, the updater can conclude that it
! 623: * has added a new name whose only RRs are the A and DHCID RR records.
! 624: * The A RR update is now complete (and a client updater is finished,
! 625: * while a server might proceed to perform a PTR RR update).
! 626: * -- "Interaction between DHCP and DNS"
! 627: */
! 628:
! 629: if (result == ISC_R_SUCCESS) {
! 630: log_info ("Added new forward map from %.*s to %s",
! 631: (int)ddns_fwd_name -> len,
! 632: (const char *)ddns_fwd_name -> data, ddns_address);
! 633: goto error;
! 634: }
! 635:
! 636:
! 637: /*
! 638: * If the first update operation fails with YXDOMAIN, the updater can
! 639: * conclude that the intended name is in use. The updater then
! 640: * attempts to confirm that the DNS name is not being used by some
! 641: * other host. The updater prepares a second UPDATE query in which the
! 642: * prerequisite is that the desired name has attached to it a DHCID RR
! 643: * whose contents match the client identity. The update section of
! 644: * this query deletes the existing A records on the name, and adds the
! 645: * A record that matches the DHCP binding and the DHCID RR with the
! 646: * client identity.
! 647: * -- "Interaction between DHCP and DNS"
! 648: */
! 649:
! 650: if (result != (rrsetp ? ISC_R_YXRRSET : ISC_R_YXDOMAIN)) {
! 651: log_error ("Unable to add forward map from %.*s to %s: %s",
! 652: (int)ddns_fwd_name -> len,
! 653: (const char *)ddns_fwd_name -> data, ddns_address,
! 654: isc_result_totext (result));
! 655: goto error;
! 656: }
! 657:
! 658: while (!ISC_LIST_EMPTY (updqueue)) {
! 659: updrec = ISC_LIST_HEAD (updqueue);
! 660: ISC_LIST_UNLINK (updqueue, updrec, r_link);
! 661: minires_freeupdrec (updrec);
! 662: }
! 663:
! 664: /* If we're doing conflict resolution, we use a set of prereqs. If
! 665: * not, we delete the DHCID in addition to all A rrsets.
! 666: */
! 667: if (conflict) {
! 668: /*
! 669: * DHCID RR exists, and matches client identity.
! 670: */
! 671: updrec = minires_mkupdrec (S_PREREQ,
! 672: (const char *)ddns_fwd_name -> data,
! 673: C_IN, T_DHCID, 0);
! 674: if (!updrec) {
! 675: result = ISC_R_NOMEMORY;
! 676: goto error;
! 677: }
! 678:
! 679: updrec -> r_data = ddns_dhcid -> data;
! 680: updrec -> r_size = ddns_dhcid -> len;
! 681: updrec -> r_opcode = YXRRSET;
! 682:
! 683: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 684: } else {
! 685: /*
! 686: * Conflict detection override: delete DHCID RRs.
! 687: */
! 688: updrec = minires_mkupdrec(S_UPDATE,
! 689: (const char *)ddns_fwd_name->data,
! 690: C_IN, T_DHCID, 0);
! 691:
! 692: if (!updrec) {
! 693: result = ISC_R_NOMEMORY;
! 694: goto error;
! 695: }
! 696:
! 697: updrec->r_data = NULL;
! 698: updrec->r_size = 0;
! 699: updrec->r_opcode = DELETE;
! 700:
! 701: ISC_LIST_APPEND(updqueue, updrec, r_link);
! 702:
! 703:
! 704: /*
! 705: * With all other DHCID RR's deleted, add this client's
! 706: * DHCID unconditionally (as update-conflict-detection is
! 707: * disabled).
! 708: */
! 709: updrec = minires_mkupdrec(S_UPDATE,
! 710: (const char *)ddns_fwd_name->data,
! 711: C_IN, T_DHCID, ttl);
! 712: if (!updrec) {
! 713: result = ISC_R_NOMEMORY;
! 714: goto error;
! 715: }
! 716:
! 717: updrec->r_data = ddns_dhcid->data;
! 718: updrec->r_size = ddns_dhcid->len;
! 719: updrec->r_opcode = ADD;
! 720:
! 721: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 722: }
! 723:
! 724:
! 725: /*
! 726: * Delete A RRset.
! 727: */
! 728: updrec = minires_mkupdrec (S_UPDATE,
! 729: (const char *)ddns_fwd_name -> data,
! 730: C_IN, ddns_address_type, 0);
! 731: if (!updrec) {
! 732: result = ISC_R_NOMEMORY;
! 733: goto error;
! 734: }
! 735:
! 736: updrec -> r_data = (unsigned char *)0;
! 737: updrec -> r_size = 0;
! 738: updrec -> r_opcode = DELETE;
! 739:
! 740: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 741:
! 742:
! 743: /*
! 744: * Add A RR.
! 745: */
! 746: updrec = minires_mkupdrec (S_UPDATE,
! 747: (const char *)ddns_fwd_name -> data,
! 748: C_IN, ddns_address_type, ttl);
! 749: if (!updrec) {
! 750: result = ISC_R_NOMEMORY;
! 751: goto error;
! 752: }
! 753:
! 754: updrec -> r_data = (unsigned char *)ddns_address;
! 755: updrec -> r_size = strlen (ddns_address);
! 756: updrec -> r_opcode = ADD;
! 757:
! 758: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 759:
! 760:
! 761: /*
! 762: * Attempt to perform the update.
! 763: */
! 764: result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
! 765:
! 766: switch (result) {
! 767: case ISC_R_SUCCESS:
! 768: logstr = NULL;
! 769: break;
! 770:
! 771: case ISC_R_YXRRSET:
! 772: case ISC_R_YXDOMAIN:
! 773: logstr = "DHCID mismatch, belongs to another client.";
! 774: break;
! 775:
! 776: case ISC_R_NXRRSET:
! 777: case ISC_R_NXDOMAIN:
! 778: logstr = "Has an address record but no DHCID, not mine.";
! 779: break;
! 780:
! 781: default:
! 782: logstr = isc_result_totext(result);
! 783: break;
! 784: }
! 785:
! 786: if (logstr != NULL)
! 787: log_error("Forward map from %.*s to %s FAILED: %s",
! 788: (int)ddns_fwd_name -> len,
! 789: (const char *)ddns_fwd_name -> data,
! 790: ddns_address, logstr);
! 791: else
! 792: log_info("Added new forward map from %.*s to %s",
! 793: (int)ddns_fwd_name -> len,
! 794: (const char *)ddns_fwd_name -> data, ddns_address);
! 795:
! 796: #if defined (DEBUG_DNS_UPDATES)
! 797: print_dns_status ((int)result, &updqueue);
! 798: #endif
! 799:
! 800: /*
! 801: * If this query succeeds, the updater can conclude that the current
! 802: * client was the last client associated with the domain name, and that
! 803: * the name now contains the updated A RR. The A RR update is now
! 804: * complete (and a client updater is finished, while a server would
! 805: * then proceed to perform a PTR RR update).
! 806: * -- "Interaction between DHCP and DNS"
! 807: */
! 808:
! 809: /*
! 810: * If the second query fails with NXRRSET, the updater must conclude
! 811: * that the client's desired name is in use by another host. At this
! 812: * juncture, the updater can decide (based on some administrative
! 813: * configuration outside of the scope of this document) whether to let
! 814: * the existing owner of the name keep that name, and to (possibly)
! 815: * perform some name disambiguation operation on behalf of the current
! 816: * client, or to replace the RRs on the name with RRs that represent
! 817: * the current client. If the configured policy allows replacement of
! 818: * existing records, the updater submits a query that deletes the
! 819: * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
! 820: * represent the IP address and client-identity of the new client.
! 821: * -- "Interaction between DHCP and DNS"
! 822: */
! 823:
! 824: error:
! 825: while (!ISC_LIST_EMPTY (updqueue)) {
! 826: updrec = ISC_LIST_HEAD (updqueue);
! 827: ISC_LIST_UNLINK (updqueue, updrec, r_link);
! 828: minires_freeupdrec (updrec);
! 829: }
! 830:
! 831: return result;
! 832: }
! 833:
! 834: isc_result_t
! 835: ddns_remove_fwd(struct data_string *ddns_fwd_name,
! 836: struct iaddr ddns_addr,
! 837: struct data_string *ddns_dhcid) {
! 838: ns_updque updqueue;
! 839: ns_updrec *updrec;
! 840: isc_result_t result = SERVFAIL;
! 841: char ddns_address[
! 842: sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
! 843: int ddns_address_type;
! 844:
! 845: /*
! 846: * We want to delete either A or AAAA records, depending on
! 847: * whether we have an IPv4 or an IPv6 address.
! 848: */
! 849: if (ddns_addr.len == 4) {
! 850: ddns_address_type = T_A;
! 851: } else if (ddns_addr.len == 16) {
! 852: ddns_address_type = T_AAAA;
! 853: } else {
! 854: return ISC_R_INVALIDARG;
! 855: }
! 856: strcpy(ddns_address, piaddr(ddns_addr));
! 857:
! 858: /*
! 859: * The entity chosen to handle the A record for this client (either the
! 860: * client or the server) SHOULD delete the A record that was added when
! 861: * the lease was made to the client.
! 862: *
! 863: * In order to perform this delete, the updater prepares an UPDATE
! 864: * query which contains two prerequisites. The first prerequisite
! 865: * asserts that the DHCID RR exists whose data is the client identity
! 866: * described in Section 4.3. The second prerequisite asserts that the
! 867: * data in the A RR contains the IP address of the lease that has
! 868: * expired or been released.
! 869: * -- "Interaction between DHCP and DNS"
! 870: */
! 871:
! 872: ISC_LIST_INIT (updqueue);
! 873:
! 874: /*
! 875: * DHCID RR exists, and matches client identity.
! 876: */
! 877: updrec = minires_mkupdrec (S_PREREQ,
! 878: (const char *)ddns_fwd_name -> data,
! 879: C_IN, T_DHCID,0);
! 880: if (!updrec) {
! 881: result = ISC_R_NOMEMORY;
! 882: goto error;
! 883: }
! 884:
! 885: updrec -> r_data = ddns_dhcid -> data;
! 886: updrec -> r_size = ddns_dhcid -> len;
! 887: updrec -> r_opcode = YXRRSET;
! 888:
! 889: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 890:
! 891:
! 892: /*
! 893: * Address RR (A/AAAA) matches the expiring lease.
! 894: */
! 895: updrec = minires_mkupdrec (S_PREREQ,
! 896: (const char *)ddns_fwd_name -> data,
! 897: C_IN, ddns_address_type, 0);
! 898: if (!updrec) {
! 899: result = ISC_R_NOMEMORY;
! 900: goto error;
! 901: }
! 902:
! 903: updrec -> r_data = (unsigned char *)ddns_address;
! 904: updrec -> r_size = strlen (ddns_address);
! 905: updrec -> r_opcode = YXRRSET;
! 906:
! 907: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 908:
! 909:
! 910: /*
! 911: * Delete appropriate Address RR (A/AAAA).
! 912: */
! 913: updrec = minires_mkupdrec (S_UPDATE,
! 914: (const char *)ddns_fwd_name -> data,
! 915: C_IN, ddns_address_type, 0);
! 916: if (!updrec) {
! 917: result = ISC_R_NOMEMORY;
! 918: goto error;
! 919: }
! 920:
! 921: updrec -> r_data = (unsigned char *)ddns_address;
! 922: updrec -> r_size = strlen (ddns_address);
! 923: updrec -> r_opcode = DELETE;
! 924:
! 925: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 926:
! 927: /*
! 928: * Attempt to perform the update.
! 929: */
! 930: result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
! 931: print_dns_status ((int)result, &updqueue);
! 932:
! 933: /*
! 934: * If the query fails, the updater MUST NOT delete the DNS name. It
! 935: * may be that the host whose lease on the server has expired has moved
! 936: * to another network and obtained a lease from a different server,
! 937: * which has caused the client's A RR to be replaced. It may also be
! 938: * that some other client has been configured with a name that matches
! 939: * the name of the DHCP client, and the policy was that the last client
! 940: * to specify the name would get the name. In this case, the DHCID RR
! 941: * will no longer match the updater's notion of the client-identity of
! 942: * the host pointed to by the DNS name.
! 943: * -- "Interaction between DHCP and DNS"
! 944: */
! 945:
! 946: if (result != ISC_R_SUCCESS) {
! 947: /* If the rrset isn't there, we didn't need to do the
! 948: delete, which is success. */
! 949: if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
! 950: result = ISC_R_SUCCESS;
! 951: goto error;
! 952: }
! 953:
! 954: while (!ISC_LIST_EMPTY (updqueue)) {
! 955: updrec = ISC_LIST_HEAD (updqueue);
! 956: ISC_LIST_UNLINK (updqueue, updrec, r_link);
! 957: minires_freeupdrec (updrec);
! 958: }
! 959:
! 960: /*
! 961: * If the deletion of the desired address succeeded (its A or AAAA
! 962: * RR was removed above), and there are zero other A or AAAA records
! 963: * left for this domain, then we can delete the DHCID record as well.
! 964: * We can't delete the DHCID record above because it's possible the
! 965: * client has more than one valid address added to this domain name,
! 966: * by this or other DHCP servers.
! 967: *
! 968: * Essentially, this final update is a cleanup operation that is only
! 969: * intended to succeed after the last address has been removed from
! 970: * DNS (which is only expected to happen after the client is not
! 971: * reasonably in possession of those addresses).
! 972: */
! 973: ISC_LIST_INIT (updqueue);
! 974:
! 975: /*
! 976: * A RR does not exist.
! 977: */
! 978: updrec = minires_mkupdrec(S_PREREQ, (const char *)ddns_fwd_name->data,
! 979: C_IN, T_A, 0);
! 980: if (updrec == NULL) {
! 981: result = ISC_R_NOMEMORY;
! 982: goto error;
! 983: }
! 984:
! 985: updrec->r_data = NULL;
! 986: updrec->r_size = 0;
! 987: updrec->r_opcode = NXRRSET;
! 988:
! 989: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 990:
! 991: /*
! 992: * AAAA RR does not exist.
! 993: */
! 994: updrec = minires_mkupdrec(S_PREREQ, (const char *)ddns_fwd_name->data,
! 995: C_IN, T_AAAA, 0);
! 996:
! 997: if (updrec == NULL) {
! 998: result = ISC_R_NOMEMORY;
! 999: goto error;
! 1000: }
! 1001:
! 1002: updrec->r_data = NULL;
! 1003: updrec->r_size = 0;
! 1004: updrec->r_opcode = NXRRSET;
! 1005:
! 1006: ISC_LIST_APPEND(updqueue, updrec, r_link);
! 1007:
! 1008: /*
! 1009: * Delete appropriate DHCID RR.
! 1010: */
! 1011: updrec = minires_mkupdrec (S_UPDATE,
! 1012: (const char *)ddns_fwd_name -> data,
! 1013: C_IN, T_DHCID, 0);
! 1014: if (!updrec) {
! 1015: result = ISC_R_NOMEMORY;
! 1016: goto error;
! 1017: }
! 1018:
! 1019: updrec -> r_data = ddns_dhcid -> data;
! 1020: updrec -> r_size = ddns_dhcid -> len;
! 1021: updrec -> r_opcode = DELETE;
! 1022:
! 1023: ISC_LIST_APPEND (updqueue, updrec, r_link);
! 1024:
! 1025: /*
! 1026: * Attempt to perform the update.
! 1027: */
! 1028: result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
! 1029: print_dns_status ((int)result, &updqueue);
! 1030:
! 1031: /* Fall through. */
! 1032: error:
! 1033:
! 1034: while (!ISC_LIST_EMPTY (updqueue)) {
! 1035: updrec = ISC_LIST_HEAD (updqueue);
! 1036: ISC_LIST_UNLINK (updqueue, updrec, r_link);
! 1037: minires_freeupdrec (updrec);
! 1038: }
! 1039:
! 1040: return result;
! 1041: }
! 1042:
! 1043:
! 1044: #endif /* NSUPDATE */
! 1045:
! 1046: HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
! 1047: dns_zone_reference, dns_zone_dereference, do_case_hash)
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>