File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dhcp / common / dns.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:06:54 2012 UTC (12 years ago) by misho
Branches: dhcp, MAIN
CVS tags: v4_1_R7p0, v4_1_R7, v4_1_R4, HEAD
dhcp 4.1 r7

    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>