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

    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>