Home | History | Annotate | Line # | Download | only in server
ddns.c revision 1.1
      1  1.1  christos /*	$NetBSD: ddns.c,v 1.1 2018/04/07 22:34:27 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /* ddns.c
      4  1.1  christos 
      5  1.1  christos    Dynamic DNS updates. */
      6  1.1  christos 
      7  1.1  christos /*
      8  1.1  christos  *
      9  1.1  christos  * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
     10  1.1  christos  * Copyright (c) 2000-2003 by Internet Software Consortium
     11  1.1  christos  *
     12  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
     13  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     14  1.1  christos  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     15  1.1  christos  *
     16  1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     17  1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     18  1.1  christos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     19  1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     20  1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     21  1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     22  1.1  christos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     23  1.1  christos  *
     24  1.1  christos  *   Internet Systems Consortium, Inc.
     25  1.1  christos  *   950 Charter Street
     26  1.1  christos  *   Redwood City, CA 94063
     27  1.1  christos  *   <info (at) isc.org>
     28  1.1  christos  *   https://www.isc.org/
     29  1.1  christos  *
     30  1.1  christos  * This software has been donated to Internet Systems Consortium
     31  1.1  christos  * by Damien Neil of Nominum, Inc.
     32  1.1  christos  *
     33  1.1  christos  * To learn more about Internet Systems Consortium, see
     34  1.1  christos  * ``https://www.isc.org/''.
     35  1.1  christos  */
     36  1.1  christos 
     37  1.1  christos #include <sys/cdefs.h>
     38  1.1  christos __RCSID("$NetBSD: ddns.c,v 1.1 2018/04/07 22:34:27 christos Exp $");
     39  1.1  christos 
     40  1.1  christos #include "dhcpd.h"
     41  1.1  christos #include <dns/result.h>
     42  1.1  christos 
     43  1.1  christos char *ddns_standard_tag = "ddns-dhcid";
     44  1.1  christos char *ddns_interim_tag  = "ddns-txt";
     45  1.1  christos 
     46  1.1  christos #ifdef NSUPDATE
     47  1.1  christos 
     48  1.1  christos #if defined (DEBUG_DNS_UPDATES)
     49  1.1  christos static char* dump_ddns_cb_func(void *func);
     50  1.1  christos static char* dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb);
     51  1.1  christos 
     52  1.1  christos extern struct enumeration_value ddns_styles_values[];
     53  1.1  christos #endif
     54  1.1  christos 
     55  1.1  christos static void ddns_fwd_srv_connector(struct lease          *lease,
     56  1.1  christos 				   struct iasubopt       *lease6,
     57  1.1  christos 				   struct binding_scope **inscope,
     58  1.1  christos 				   dhcp_ddns_cb_t        *ddns_cb,
     59  1.1  christos 				   isc_result_t           eresult);
     60  1.1  christos 
     61  1.1  christos static void copy_conflict_flags(u_int16_t *target, u_int16_t source);
     62  1.1  christos 
     63  1.1  christos static void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult);
     64  1.1  christos 
     65  1.1  christos /*
     66  1.1  christos  * ddns_cb_free() is part of common lib, while ia_* routines are known
     67  1.1  christos  * only in the server.  Use this wrapper instead of ddns_cb_free() directly.
     68  1.1  christos  */
     69  1.1  christos static void
     70  1.1  christos destroy_ddns_cb(struct dhcp_ddns_cb *ddns_cb, char* file, int line) {
     71  1.1  christos 	if (!ddns_cb) {
     72  1.1  christos 		return;
     73  1.1  christos 	}
     74  1.1  christos 
     75  1.1  christos         if (ddns_cb->fixed6_ia) {
     76  1.1  christos                 ia_dereference(&ddns_cb->fixed6_ia, MDL);
     77  1.1  christos         }
     78  1.1  christos 
     79  1.1  christos 	ddns_cb_free(ddns_cb, file, line);
     80  1.1  christos 
     81  1.1  christos }
     82  1.1  christos 
     83  1.1  christos 
     84  1.1  christos /* DN: No way of checking that there is enough space in a data_string's
     85  1.1  christos    buffer.  Be certain to allocate enough!
     86  1.1  christos    TL: This is why the expression evaluation code allocates a *new*
     87  1.1  christos    data_string.   :') */
     88  1.1  christos static void data_string_append (struct data_string *ds1,
     89  1.1  christos 				struct data_string *ds2)
     90  1.1  christos {
     91  1.1  christos 	memcpy (ds1 -> buffer -> data + ds1 -> len,
     92  1.1  christos 		ds2 -> data,
     93  1.1  christos 		ds2 -> len);
     94  1.1  christos 	ds1 -> len += ds2 -> len;
     95  1.1  christos }
     96  1.1  christos 
     97  1.1  christos 
     98  1.1  christos /* Determine what, if any, forward and reverse updates need to be
     99  1.1  christos  * performed, and carry them through.
    100  1.1  christos  */
    101  1.1  christos int
    102  1.1  christos ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
    103  1.1  christos 	     struct iasubopt *lease6, struct iasubopt *old6,
    104  1.1  christos 	     struct option_state *options)
    105  1.1  christos {
    106  1.1  christos 	unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
    107  1.1  christos 	struct data_string ddns_hostname;
    108  1.1  christos 	struct data_string ddns_domainname;
    109  1.1  christos 	struct data_string old_ddns_fwd_name;
    110  1.1  christos 	struct data_string ddns_fwd_name;
    111  1.1  christos 	struct data_string ddns_dhcid;
    112  1.1  christos 	struct binding_scope **scope = NULL;
    113  1.1  christos 	struct data_string d1;
    114  1.1  christos 	struct option_cache *oc;
    115  1.1  christos 	int s1, s2;
    116  1.1  christos 	int result = 0;
    117  1.1  christos 	int server_updates_a = 1;
    118  1.1  christos 	struct buffer *bp = (struct buffer *)0;
    119  1.1  christos 	int ignorep = 0, client_ignorep = 0;
    120  1.1  christos 	int rev_name_len;
    121  1.1  christos 	int i;
    122  1.1  christos 
    123  1.1  christos 	dhcp_ddns_cb_t *ddns_cb;
    124  1.1  christos 	int do_remove = 0;
    125  1.1  christos 
    126  1.1  christos 	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
    127  1.1  christos 	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
    128  1.1  christos 		return (0);
    129  1.1  christos 
    130  1.1  christos 	/*
    131  1.1  christos 	 * sigh, I want to cancel any previous udpates before we do anything
    132  1.1  christos 	 * else but this means we need to deal with the lease vs lease6
    133  1.1  christos 	 * question twice.
    134  1.1  christos 	 * If there is a ddns request already outstanding cancel it.
    135  1.1  christos 	 */
    136  1.1  christos 
    137  1.1  christos 	if (lease != NULL) {
    138  1.1  christos 		if ((old != NULL) && (old->ddns_cb != NULL)) {
    139  1.1  christos 			ddns_cancel(old->ddns_cb, MDL);
    140  1.1  christos 			old->ddns_cb = NULL;
    141  1.1  christos 		}
    142  1.1  christos 	} else if (lease6 != NULL) {
    143  1.1  christos 		if ((old6 != NULL) && (old6->ddns_cb != NULL)) {
    144  1.1  christos 			ddns_cancel(old6->ddns_cb, MDL);
    145  1.1  christos 			old6->ddns_cb = NULL;
    146  1.1  christos 		}
    147  1.1  christos 	} else {
    148  1.1  christos 		log_fatal("Impossible condition at %s:%d.", MDL);
    149  1.1  christos 		/* Silence compiler warnings. */
    150  1.1  christos 		result = 0;
    151  1.1  christos 		return(0);
    152  1.1  christos 	}
    153  1.1  christos 
    154  1.1  christos 	/* allocate our control block */
    155  1.1  christos 	ddns_cb = ddns_cb_alloc(MDL);
    156  1.1  christos 	if (ddns_cb == NULL) {
    157  1.1  christos 		return(0);
    158  1.1  christos 	}
    159  1.1  christos 	/*
    160  1.1  christos 	 * Assume that we shall update both the A and ptr records and,
    161  1.1  christos 	 * as this is an update, set the active flag
    162  1.1  christos 	 */
    163  1.1  christos 	ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR |
    164  1.1  christos 		DDNS_ACTIVE_LEASE;
    165  1.1  christos 
    166  1.1  christos 	/*
    167  1.1  christos 	 * For v4 we flag static leases so we don't try
    168  1.1  christos 	 * and manipulate the lease later.  For v6 we don't
    169  1.1  christos 	 * get static leases and don't need to flag them.
    170  1.1  christos 	 */
    171  1.1  christos 	if (lease != NULL) {
    172  1.1  christos 		scope = &(lease->scope);
    173  1.1  christos 		ddns_cb->address = lease->ip_addr;
    174  1.1  christos 		if (lease->flags & STATIC_LEASE)
    175  1.1  christos 			ddns_cb->flags |= DDNS_STATIC_LEASE;
    176  1.1  christos 	} else if (lease6 != NULL) {
    177  1.1  christos 		scope = &(lease6->scope);
    178  1.1  christos 		memcpy(ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
    179  1.1  christos 		ddns_cb->address.len = 16;
    180  1.1  christos 
    181  1.1  christos 		if (lease6->static_lease) {
    182  1.1  christos 			/* We add a reference to keep ia && iasubopt alive
    183  1.1  christos 			* since static v6s are retained anywhere */
    184  1.1  christos 			ia_reference(&ddns_cb->fixed6_ia, lease6->ia, MDL);
    185  1.1  christos 			ddns_cb->flags |= DDNS_STATIC_LEASE;
    186  1.1  christos 		}
    187  1.1  christos 	}
    188  1.1  christos 
    189  1.1  christos 	memset (&d1, 0, sizeof(d1));
    190  1.1  christos 	memset (&ddns_hostname, 0, sizeof (ddns_hostname));
    191  1.1  christos 	memset (&ddns_domainname, 0, sizeof (ddns_domainname));
    192  1.1  christos 	memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
    193  1.1  christos 	memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
    194  1.1  christos 	memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
    195  1.1  christos 
    196  1.1  christos 	/* If we are allowed to accept the client's update of its own A
    197  1.1  christos 	   record, see if the client wants to update its own A record. */
    198  1.1  christos 	if (!(oc = lookup_option(&server_universe, options,
    199  1.1  christos 				 SV_CLIENT_UPDATES)) ||
    200  1.1  christos 	    evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
    201  1.1  christos 					  packet->options, options, scope,
    202  1.1  christos 					  oc, MDL)) {
    203  1.1  christos 		/* If there's no fqdn.no-client-update or if it's
    204  1.1  christos 		   nonzero, don't try to use the client-supplied
    205  1.1  christos 		   XXX */
    206  1.1  christos 		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
    207  1.1  christos 					  FQDN_SERVER_UPDATE)) ||
    208  1.1  christos 		    evaluate_boolean_option_cache(&ignorep, packet, lease,
    209  1.1  christos 						  NULL, packet->options,
    210  1.1  christos 						  options, scope, oc, MDL))
    211  1.1  christos 			goto noclient;
    212  1.1  christos 		/* Win98 and Win2k will happily claim to be willing to
    213  1.1  christos 		   update an unqualified domain name. */
    214  1.1  christos 		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
    215  1.1  christos 					  FQDN_DOMAINNAME)))
    216  1.1  christos 			goto noclient;
    217  1.1  christos 		if (!(oc = lookup_option (&fqdn_universe, packet -> options,
    218  1.1  christos 					  FQDN_FQDN)) ||
    219  1.1  christos 		    !evaluate_option_cache(&ddns_fwd_name, packet, lease,
    220  1.1  christos 					   NULL, packet->options,
    221  1.1  christos 					   options, scope, oc, MDL))
    222  1.1  christos 			goto noclient;
    223  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
    224  1.1  christos 		server_updates_a = 0;
    225  1.1  christos 		goto client_updates;
    226  1.1  christos 	}
    227  1.1  christos       noclient:
    228  1.1  christos 	/* If do-forward-updates is disabled, this basically means don't
    229  1.1  christos 	   do an update unless the client is participating, so if we get
    230  1.1  christos 	   here and do-forward-updates is disabled, we can stop. */
    231  1.1  christos 	if ((oc = lookup_option (&server_universe, options,
    232  1.1  christos 				 SV_DO_FORWARD_UPDATES)) &&
    233  1.1  christos 	    !evaluate_boolean_option_cache(&ignorep, packet, lease,
    234  1.1  christos 					   NULL, packet->options,
    235  1.1  christos 					   options, scope, oc, MDL)) {
    236  1.1  christos 		goto out;
    237  1.1  christos 	}
    238  1.1  christos 
    239  1.1  christos 	/* If it's a static lease, then don't do the DNS update unless we're
    240  1.1  christos 	   specifically configured to do so.   If the client asked to do its
    241  1.1  christos 	   own update and we allowed that, we don't do this test. */
    242  1.1  christos 	/* XXX: note that we cannot detect static DHCPv6 leases. */
    243  1.1  christos 	if ((lease != NULL) && (lease->flags & STATIC_LEASE)) {
    244  1.1  christos 		if (!(oc = lookup_option(&server_universe, options,
    245  1.1  christos 					 SV_UPDATE_STATIC_LEASES)) ||
    246  1.1  christos 		    !evaluate_boolean_option_cache(&ignorep, packet, lease,
    247  1.1  christos 						   NULL, packet->options,
    248  1.1  christos 						   options, scope, oc, MDL))
    249  1.1  christos 			goto out;
    250  1.1  christos 	}
    251  1.1  christos 
    252  1.1  christos 	/*
    253  1.1  christos 	 * Compute the name for the A record.
    254  1.1  christos 	 */
    255  1.1  christos 	oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
    256  1.1  christos 	if (oc)
    257  1.1  christos 		s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
    258  1.1  christos 					   NULL, packet->options,
    259  1.1  christos 					   options, scope, oc, MDL);
    260  1.1  christos 	else
    261  1.1  christos 		s1 = 0;
    262  1.1  christos 
    263  1.1  christos 	/* If we don't have a host name based on ddns-hostname then use
    264  1.1  christos 	 * the host declaration name if there is one and use-host-decl-names
    265  1.1  christos 	 * is turned on. */
    266  1.1  christos 	if ((s1 == 0) && (lease && lease->host && lease->host->name)) {
    267  1.1  christos 		oc = lookup_option(&server_universe, options,
    268  1.1  christos 				   SV_USE_HOST_DECL_NAMES);
    269  1.1  christos 		if (evaluate_boolean_option_cache(NULL, packet, lease,
    270  1.1  christos 						  NULL, packet->options,
    271  1.1  christos 						  options, scope, oc, MDL)) {
    272  1.1  christos 			s1 = ((data_string_new(&ddns_hostname,
    273  1.1  christos 					      lease->host->name,
    274  1.1  christos 					      strlen(lease->host->name),
    275  1.1  christos                                               MDL) && ddns_hostname.len > 0));
    276  1.1  christos 		}
    277  1.1  christos 	}
    278  1.1  christos 
    279  1.1  christos 	oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
    280  1.1  christos 	if (oc)
    281  1.1  christos 		s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
    282  1.1  christos 					   NULL, packet->options,
    283  1.1  christos 					   options, scope, oc, MDL);
    284  1.1  christos 	else
    285  1.1  christos 		s2 = 0;
    286  1.1  christos 
    287  1.1  christos 	if (s1 && s2) {
    288  1.1  christos 		if (ddns_hostname.len + ddns_domainname.len > 253) {
    289  1.1  christos 			log_error ("ddns_update: host.domain name too long");
    290  1.1  christos 
    291  1.1  christos 			goto out;
    292  1.1  christos 		}
    293  1.1  christos 
    294  1.1  christos 		if (buffer_allocate (&ddns_fwd_name.buffer,
    295  1.1  christos 				     ddns_hostname.len +
    296  1.1  christos 				     ddns_domainname.len + 2, MDL)) {
    297  1.1  christos 			ddns_fwd_name.data = ddns_fwd_name.buffer->data;
    298  1.1  christos 			data_string_append (&ddns_fwd_name, &ddns_hostname);
    299  1.1  christos 			ddns_fwd_name.buffer->data[ddns_fwd_name.len] = '.';
    300  1.1  christos 			ddns_fwd_name.len++;
    301  1.1  christos 			data_string_append (&ddns_fwd_name, &ddns_domainname);
    302  1.1  christos 			ddns_fwd_name.buffer->data[ddns_fwd_name.len] ='\0';
    303  1.1  christos 			ddns_fwd_name.terminated = 1;
    304  1.1  christos 		}
    305  1.1  christos 	}
    306  1.1  christos       client_updates:
    307  1.1  christos 
    308  1.1  christos 	/* See if there's a name already stored on the lease. */
    309  1.1  christos 	if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) {
    310  1.1  christos 		/* If there is, see if it's different. */
    311  1.1  christos 		if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
    312  1.1  christos 		    memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
    313  1.1  christos 			    old_ddns_fwd_name.len)) {
    314  1.1  christos 			/*
    315  1.1  christos 			 * If the name is different, mark the old record
    316  1.1  christos 			 * for deletion and continue getting the new info.
    317  1.1  christos 			 */
    318  1.1  christos 			do_remove = 1;
    319  1.1  christos 			goto in;
    320  1.1  christos 		}
    321  1.1  christos 
    322  1.1  christos #if defined  (DDNS_UPDATE_SLOW_TRANSITION)
    323  1.1  christos 		/*
    324  1.1  christos 		 * If the slow transition code is enabled check to see
    325  1.1  christos 		 * if the stored type (standard or interim doesn't
    326  1.1  christos 		 * match the type currently in use.  If it doesn't
    327  1.1  christos 		 * try to remove and replace the DNS record
    328  1.1  christos 		 */
    329  1.1  christos 		if (((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
    330  1.1  christos 		     find_bound_string(&ddns_dhcid, *scope, ddns_interim_tag)) ||
    331  1.1  christos 		    ((ddns_update_style == DDNS_UPDATE_STYLE_INTERIM) &&
    332  1.1  christos 		     find_bound_string(&ddns_dhcid, *scope, ddns_standard_tag))) {
    333  1.1  christos 			data_string_forget(&ddns_dhcid, MDL);
    334  1.1  christos 			do_remove = 1;
    335  1.1  christos 			goto in;
    336  1.1  christos 		}
    337  1.1  christos #endif
    338  1.1  christos 
    339  1.1  christos 		/* See if the administrator wants to do updates even
    340  1.1  christos 		   in cases where the update already appears to have been
    341  1.1  christos 		   done. */
    342  1.1  christos 		if (!(oc = lookup_option(&server_universe, options,
    343  1.1  christos 					 SV_UPDATE_OPTIMIZATION)) ||
    344  1.1  christos 		    evaluate_boolean_option_cache(&ignorep, packet, lease,
    345  1.1  christos 						  NULL, packet->options,
    346  1.1  christos 						  options, scope, oc, MDL)) {
    347  1.1  christos 			result = 1;
    348  1.1  christos 			goto noerror;
    349  1.1  christos 		}
    350  1.1  christos 	/* If there's no "ddns-fwd-name" on the lease record, see if
    351  1.1  christos 	 * there's a ddns-client-fqdn indicating a previous client
    352  1.1  christos 	 * update (if it changes, we need to adjust the PTR).
    353  1.1  christos 	 */
    354  1.1  christos 	} else if (find_bound_string(&old_ddns_fwd_name, *scope,
    355  1.1  christos 				     "ddns-client-fqdn")) {
    356  1.1  christos 		/* If the name is not different, no need to update
    357  1.1  christos 		   the PTR record. */
    358  1.1  christos 		if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
    359  1.1  christos 		    !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
    360  1.1  christos 			     old_ddns_fwd_name.len) &&
    361  1.1  christos 		    (!(oc = lookup_option(&server_universe, options,
    362  1.1  christos 					  SV_UPDATE_OPTIMIZATION)) ||
    363  1.1  christos 		     evaluate_boolean_option_cache(&ignorep, packet, lease,
    364  1.1  christos 						   NULL, packet->options,
    365  1.1  christos 						   options, scope, oc, MDL))) {
    366  1.1  christos 			goto noerror;
    367  1.1  christos 		}
    368  1.1  christos 	}
    369  1.1  christos       in:
    370  1.1  christos 
    371  1.1  christos 	/* If we don't have a name that the client has been assigned, we
    372  1.1  christos 	   can just skip all this. */
    373  1.1  christos 
    374  1.1  christos 	if ((!ddns_fwd_name.len) || (ddns_fwd_name.len > 255)) {
    375  1.1  christos 		if (ddns_fwd_name.len > 255) {
    376  1.1  christos 			log_error ("client provided fqdn: too long");
    377  1.1  christos 		}
    378  1.1  christos 
    379  1.1  christos 		/* If desired do the removals */
    380  1.1  christos 		if (do_remove != 0) {
    381  1.1  christos 			(void) ddns_removals(lease, lease6, NULL, ISC_TRUE);
    382  1.1  christos 		}
    383  1.1  christos 		goto out;
    384  1.1  christos 	}
    385  1.1  christos 
    386  1.1  christos 	/*
    387  1.1  christos 	 * Compute the RR TTL.
    388  1.1  christos 	 *
    389  1.1  christos 	 * We have two ways of computing the TTL.
    390  1.1  christos 	 * The old behavior was to allow for the customer to set up
    391  1.1  christos 	 * the option or to default things.  For v4 this was 1/2
    392  1.1  christos 	 * of the lease time, for v6 this was DEFAULT_DDNS_TTL.
    393  1.1  christos 	 * The new behavior continues to allow the customer to set
    394  1.1  christos 	 * up an option but the defaults are a little different.
    395  1.1  christos 	 * We now use 1/2 of the (preferred) lease time for both
    396  1.1  christos 	 * v4 and v6 and cap them at a maximum value.
    397  1.1  christos 	 * If the customer chooses to use an experession that references
    398  1.1  christos 	 * part of the lease the v6 value will be the default as there
    399  1.1  christos 	 * isn't a lease available for v6.
    400  1.1  christos 	 */
    401  1.1  christos 
    402  1.1  christos 	ddns_ttl = DEFAULT_DDNS_TTL;
    403  1.1  christos 	if (lease != NULL) {
    404  1.1  christos 		if (lease->ends <= cur_time) {
    405  1.1  christos 			ddns_ttl = 0;
    406  1.1  christos 		} else {
    407  1.1  christos 			ddns_ttl = (lease->ends - cur_time)/2;
    408  1.1  christos 		}
    409  1.1  christos 	}
    410  1.1  christos #ifndef USE_OLD_DDNS_TTL
    411  1.1  christos 	else if (lease6 != NULL) {
    412  1.1  christos 		ddns_ttl = lease6->prefer/2;
    413  1.1  christos 	}
    414  1.1  christos 
    415  1.1  christos 	if (ddns_ttl > MAX_DEFAULT_DDNS_TTL) {
    416  1.1  christos 		ddns_ttl = MAX_DEFAULT_DDNS_TTL;
    417  1.1  christos 	}
    418  1.1  christos #endif
    419  1.1  christos 
    420  1.1  christos 	if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) {
    421  1.1  christos 		if (evaluate_option_cache(&d1, packet, lease, NULL,
    422  1.1  christos 					  packet->options, options,
    423  1.1  christos 					  scope, oc, MDL)) {
    424  1.1  christos 			if (d1.len == sizeof (u_int32_t))
    425  1.1  christos 				ddns_ttl = getULong (d1.data);
    426  1.1  christos 			data_string_forget (&d1, MDL);
    427  1.1  christos 		}
    428  1.1  christos 	}
    429  1.1  christos 
    430  1.1  christos 	ddns_cb->ttl = ddns_ttl;
    431  1.1  christos 
    432  1.1  christos 	/*
    433  1.1  christos 	 * Compute the reverse IP name, starting with the domain name.
    434  1.1  christos 	 */
    435  1.1  christos 	oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
    436  1.1  christos 	if (oc)
    437  1.1  christos 		s1 = evaluate_option_cache(&d1, packet, lease, NULL,
    438  1.1  christos 					   packet->options, options,
    439  1.1  christos 					   scope, oc, MDL);
    440  1.1  christos 	else
    441  1.1  christos 		s1 = 0;
    442  1.1  christos 
    443  1.1  christos 	/*
    444  1.1  christos 	 * Figure out the length of the part of the name that depends
    445  1.1  christos 	 * on the address.
    446  1.1  christos 	 */
    447  1.1  christos 	if (ddns_cb->address.len == 4) {
    448  1.1  christos 		char buf[17];
    449  1.1  christos 		/* XXX: WOW this is gross. */
    450  1.1  christos 		rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
    451  1.1  christos 					ddns_cb->address.iabuf[3] & 0xff,
    452  1.1  christos 					ddns_cb->address.iabuf[2] & 0xff,
    453  1.1  christos 					ddns_cb->address.iabuf[1] & 0xff,
    454  1.1  christos 					ddns_cb->address.iabuf[0] & 0xff) + 1;
    455  1.1  christos 
    456  1.1  christos 		if (s1) {
    457  1.1  christos 			rev_name_len += d1.len;
    458  1.1  christos 
    459  1.1  christos 			if (rev_name_len > 255) {
    460  1.1  christos 				log_error("ddns_update: Calculated rev domain "
    461  1.1  christos 					  "name too long.");
    462  1.1  christos 				s1 = 0;
    463  1.1  christos 				data_string_forget(&d1, MDL);
    464  1.1  christos 			}
    465  1.1  christos 		}
    466  1.1  christos 	} else if (ddns_cb->address.len == 16) {
    467  1.1  christos 		/*
    468  1.1  christos 		 * IPv6 reverse names are always the same length, with
    469  1.1  christos 		 * 32 hex characters separated by dots.
    470  1.1  christos 		 */
    471  1.1  christos 		rev_name_len = sizeof("0.1.2.3.4.5.6.7."
    472  1.1  christos 				      "8.9.a.b.c.d.e.f."
    473  1.1  christos 				      "0.1.2.3.4.5.6.7."
    474  1.1  christos 				      "8.9.a.b.c.d.e.f."
    475  1.1  christos 				      "ip6.arpa.");
    476  1.1  christos 
    477  1.1  christos 		/* Set s1 to make sure we gate into updates. */
    478  1.1  christos 		s1 = 1;
    479  1.1  christos 	} else {
    480  1.1  christos 		log_fatal("invalid address length %d", ddns_cb->address.len);
    481  1.1  christos 		/* Silence compiler warnings. */
    482  1.1  christos 		return 0;
    483  1.1  christos 	}
    484  1.1  christos 
    485  1.1  christos 	/* See if we are configured NOT to do reverse ptr updates */
    486  1.1  christos 	if ((oc = lookup_option(&server_universe, options,
    487  1.1  christos 				SV_DO_REVERSE_UPDATES)) &&
    488  1.1  christos 	    !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
    489  1.1  christos 					   packet->options, options,
    490  1.1  christos 					   scope, oc, MDL)) {
    491  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_PTR;
    492  1.1  christos 	}
    493  1.1  christos 
    494  1.1  christos 	if (s1) {
    495  1.1  christos 		if (buffer_allocate(&ddns_cb->rev_name.buffer,
    496  1.1  christos 				    rev_name_len, MDL)) {
    497  1.1  christos 			struct data_string *rname = &ddns_cb->rev_name;
    498  1.1  christos 			rname->data = rname->buffer->data;
    499  1.1  christos 
    500  1.1  christos 			if (ddns_cb->address.len == 4) {
    501  1.1  christos 				rname->len =
    502  1.1  christos 				    sprintf((char *)rname->buffer->data,
    503  1.1  christos 					    "%u.%u.%u.%u.",
    504  1.1  christos 					    ddns_cb->address.iabuf[3] & 0xff,
    505  1.1  christos 					    ddns_cb->address.iabuf[2] & 0xff,
    506  1.1  christos 					    ddns_cb->address.iabuf[1] & 0xff,
    507  1.1  christos 					    ddns_cb->address.iabuf[0] & 0xff);
    508  1.1  christos 
    509  1.1  christos 				/*
    510  1.1  christos 				 * d1.data may be opaque, garbage bytes, from
    511  1.1  christos 				 * user (mis)configuration.
    512  1.1  christos 				 */
    513  1.1  christos 				data_string_append(rname, &d1);
    514  1.1  christos 				rname->buffer->data[rname->len] = '\0';
    515  1.1  christos 			} else if (ddns_cb->address.len == 16) {
    516  1.1  christos 				char *p = (char *)&rname->buffer->data;
    517  1.1  christos 				unsigned char *a = ddns_cb->address.iabuf + 15;
    518  1.1  christos 				for (i=0; i<16; i++) {
    519  1.1  christos 					sprintf(p, "%x.%x.",
    520  1.1  christos 						(*a & 0xF), ((*a >> 4) & 0xF));
    521  1.1  christos 					p += 4;
    522  1.1  christos 					a -= 1;
    523  1.1  christos 				}
    524  1.1  christos 				strcat(p, "ip6.arpa.");
    525  1.1  christos 				rname->len = strlen((const char *)rname->data);
    526  1.1  christos 			}
    527  1.1  christos 
    528  1.1  christos 			rname->terminated = 1;
    529  1.1  christos 		}
    530  1.1  christos 
    531  1.1  christos 		if (d1.data != NULL)
    532  1.1  christos 			data_string_forget(&d1, MDL);
    533  1.1  christos 	}
    534  1.1  christos 
    535  1.1  christos 	/*
    536  1.1  christos 	 * copy the string now so we can pass it to the dhcid routines
    537  1.1  christos 	 * via the ddns_cb pointer
    538  1.1  christos 	 */
    539  1.1  christos 	data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL);
    540  1.1  christos 
    541  1.1  christos 	/*
    542  1.1  christos 	 * If we are updating the A record, compute the DHCID value.
    543  1.1  christos 	 * We have two options for computing the DHCID value, the older
    544  1.1  christos 	 * interim version and the newer standard version.  The interim
    545  1.1  christos 	 * has some issues but is left as is to avoid compatibility issues.
    546  1.1  christos 	 *
    547  1.1  christos 	 * We select the type of DHCID to construct and the information to
    548  1.1  christos 	 * use for the digest based on 4701 section 3.3
    549  1.1  christos 	 */
    550  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
    551  1.1  christos 		int ddns_type;
    552  1.1  christos 		int ddns_len;
    553  1.1  christos 		if (ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) {
    554  1.1  christos 			/* The standard style */
    555  1.1  christos 			ddns_cb->lease_tag = ddns_standard_tag;
    556  1.1  christos 			ddns_cb->dhcid_class = dns_rdatatype_dhcid;
    557  1.1  christos 			ddns_cb->other_dhcid_class = dns_rdatatype_txt;
    558  1.1  christos 			ddns_type = 1;
    559  1.1  christos 			ddns_len = 4;
    560  1.1  christos 		} else {
    561  1.1  christos 			/* The older interim style */
    562  1.1  christos 			ddns_cb->lease_tag = ddns_interim_tag;
    563  1.1  christos 			ddns_cb->dhcid_class = dns_rdatatype_txt;
    564  1.1  christos 			ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
    565  1.1  christos 			/* for backwards compatibility */
    566  1.1  christos 			ddns_type = DHO_DHCP_CLIENT_IDENTIFIER;
    567  1.1  christos 			/* IAID incorrectly included */
    568  1.1  christos 			ddns_len = 0;
    569  1.1  christos 		}
    570  1.1  christos 
    571  1.1  christos 
    572  1.1  christos 		if (lease6 != NULL) {
    573  1.1  christos 			if (lease6->ia->iaid_duid.len < ddns_len)
    574  1.1  christos 				goto badfqdn;
    575  1.1  christos 			result = get_dhcid(ddns_cb, 2,
    576  1.1  christos 					   lease6->ia->iaid_duid.data + ddns_len,
    577  1.1  christos 					   lease6->ia->iaid_duid.len - ddns_len);
    578  1.1  christos 		} else if ((lease != NULL) &&
    579  1.1  christos 			   (lease->uid != NULL) &&
    580  1.1  christos 			   (lease->uid_len != 0)) {
    581  1.1  christos 			/* If this is standard check for an RFC 4361
    582  1.1  christos 			 * compliant client identifier
    583  1.1  christos 			 */
    584  1.1  christos 			if ((ddns_update_style == DDNS_UPDATE_STYLE_STANDARD) &&
    585  1.1  christos 			    (lease->uid[0] == 255)) {
    586  1.1  christos 				if (lease->uid_len < 5)
    587  1.1  christos 					goto badfqdn;
    588  1.1  christos 				result = get_dhcid(ddns_cb, 2,
    589  1.1  christos 						   lease->uid + 5,
    590  1.1  christos 						   lease->uid_len - 5);
    591  1.1  christos 			} else {
    592  1.1  christos 				result = get_dhcid(ddns_cb, ddns_type,
    593  1.1  christos 						   lease->uid,
    594  1.1  christos 						   lease->uid_len);
    595  1.1  christos 			}
    596  1.1  christos 		} else if (lease != NULL)
    597  1.1  christos 			result = get_dhcid(ddns_cb, 0,
    598  1.1  christos 					   lease->hardware_addr.hbuf,
    599  1.1  christos 					   lease->hardware_addr.hlen);
    600  1.1  christos 		else
    601  1.1  christos 			log_fatal("Impossible condition at %s:%d.", MDL);
    602  1.1  christos 
    603  1.1  christos 		if (!result)
    604  1.1  christos 			goto badfqdn;
    605  1.1  christos 	}
    606  1.1  christos 
    607  1.1  christos 	/*
    608  1.1  christos 	 * Perform updates.
    609  1.1  christos 	 */
    610  1.1  christos 
    611  1.1  christos 	if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
    612  1.1  christos 		copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
    613  1.1  christos 	}
    614  1.1  christos 
    615  1.1  christos 	/*
    616  1.1  christos 	 * Previously if we failed during the removal operations
    617  1.1  christos 	 * we skipped the fqdn option processing.  I'm not sure
    618  1.1  christos 	 * if we want to continue with that if we fail before sending
    619  1.1  christos 	 * the ddns messages.  Currently we don't.
    620  1.1  christos 	 */
    621  1.1  christos 	if (do_remove) {
    622  1.1  christos 		/*
    623  1.1  christos 		 * We should log a more specific error closer to the actual
    624  1.1  christos 		 * error if we want one. ddns_removal failure not logged here.
    625  1.1  christos 		 */
    626  1.1  christos 		 (void) ddns_removals(lease, lease6, ddns_cb, ISC_TRUE);
    627  1.1  christos 	}
    628  1.1  christos 	else {
    629  1.1  christos 		ddns_fwd_srv_connector(lease, lease6, scope, ddns_cb,
    630  1.1  christos 				       ISC_R_SUCCESS);
    631  1.1  christos 	}
    632  1.1  christos 	ddns_cb = NULL;
    633  1.1  christos 
    634  1.1  christos       noerror:
    635  1.1  christos 	/*
    636  1.1  christos 	 * If fqdn-reply option is disabled in dhcpd.conf, then don't
    637  1.1  christos 	 * send the client an FQDN option at all, even if one was requested.
    638  1.1  christos 	 * (WinXP clients allegedly misbehave if the option is present,
    639  1.1  christos 	 * refusing to handle PTR updates themselves).
    640  1.1  christos 	 */
    641  1.1  christos 	if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) &&
    642  1.1  christos   	    !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
    643  1.1  christos   					   packet->options, options,
    644  1.1  christos   					   scope, oc, MDL)) {
    645  1.1  christos   	    	goto badfqdn;
    646  1.1  christos 
    647  1.1  christos 	/* If we're ignoring client updates, then we tell a sort of 'white
    648  1.1  christos 	 * lie'.  We've already updated the name the server wants (per the
    649  1.1  christos 	 * config written by the server admin).  Now let the client do as
    650  1.1  christos 	 * it pleases with the name they supplied (if any).
    651  1.1  christos 	 *
    652  1.1  christos 	 * We only form an FQDN option this way if the client supplied an
    653  1.1  christos 	 * FQDN option that had FQDN_SERVER_UPDATE set false.
    654  1.1  christos 	 */
    655  1.1  christos 	} else if (client_ignorep &&
    656  1.1  christos 	    (oc = lookup_option(&fqdn_universe, packet->options,
    657  1.1  christos 				FQDN_SERVER_UPDATE)) &&
    658  1.1  christos 	    !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
    659  1.1  christos 					   packet->options, options,
    660  1.1  christos 					   scope, oc, MDL)) {
    661  1.1  christos 		oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN);
    662  1.1  christos 		if (oc && evaluate_option_cache(&d1, packet, lease, NULL,
    663  1.1  christos 						packet->options, options,
    664  1.1  christos 						scope, oc, MDL)) {
    665  1.1  christos 			if (d1.len == 0 ||
    666  1.1  christos 			    !buffer_allocate(&bp, d1.len + 5, MDL))
    667  1.1  christos 				goto badfqdn;
    668  1.1  christos 
    669  1.1  christos 			/* Server pretends it is not updating. */
    670  1.1  christos 			bp->data[0] = 0;
    671  1.1  christos 			if (!save_option_buffer(&fqdn_universe, options,
    672  1.1  christos 						bp, &bp->data[0], 1,
    673  1.1  christos 						FQDN_SERVER_UPDATE, 0))
    674  1.1  christos 				goto badfqdn;
    675  1.1  christos 
    676  1.1  christos 			/* Client is encouraged to update. */
    677  1.1  christos 			bp->data[1] = 0;
    678  1.1  christos 			if (!save_option_buffer(&fqdn_universe, options,
    679  1.1  christos 						bp, &bp->data[1], 1,
    680  1.1  christos 						FQDN_NO_CLIENT_UPDATE, 0))
    681  1.1  christos 				goto badfqdn;
    682  1.1  christos 
    683  1.1  christos 			/* Use the encoding of client's FQDN option. */
    684  1.1  christos 			oc = lookup_option(&fqdn_universe, packet->options,
    685  1.1  christos 					   FQDN_ENCODED);
    686  1.1  christos 			if (oc &&
    687  1.1  christos 			    evaluate_boolean_option_cache(&ignorep, packet,
    688  1.1  christos 							  lease, NULL,
    689  1.1  christos 							  packet->options,
    690  1.1  christos 							  options, scope,
    691  1.1  christos 							  oc, MDL))
    692  1.1  christos 				bp->data[2] = 1; /* FQDN is encoded. */
    693  1.1  christos 			else
    694  1.1  christos 				bp->data[2] = 0; /* FQDN is not encoded. */
    695  1.1  christos 
    696  1.1  christos 			if (!save_option_buffer(&fqdn_universe, options,
    697  1.1  christos 						bp, &bp->data[2], 1,
    698  1.1  christos 						FQDN_ENCODED, 0))
    699  1.1  christos 				goto badfqdn;
    700  1.1  christos 
    701  1.1  christos 			/* Current FQDN drafts indicate 255 is mandatory. */
    702  1.1  christos 			bp->data[3] = 255;
    703  1.1  christos 			if (!save_option_buffer(&fqdn_universe, options,
    704  1.1  christos 						bp, &bp->data[3], 1,
    705  1.1  christos 						FQDN_RCODE1, 0))
    706  1.1  christos 				goto badfqdn;
    707  1.1  christos 
    708  1.1  christos 			bp->data[4] = 255;
    709  1.1  christos 			if (!save_option_buffer(&fqdn_universe, options,
    710  1.1  christos 						bp, &bp->data[4], 1,
    711  1.1  christos 						FQDN_RCODE2, 0))
    712  1.1  christos 				goto badfqdn;
    713  1.1  christos 
    714  1.1  christos 			/* Copy in the FQDN supplied by the client.  Note well
    715  1.1  christos 			 * that the format of this option in the cache is going
    716  1.1  christos 			 * to be in text format.  If the fqdn supplied by the
    717  1.1  christos 			 * client is encoded, it is decoded into the option
    718  1.1  christos 			 * cache when parsed out of the packet.  It will be
    719  1.1  christos 			 * re-encoded when the option is assembled to be
    720  1.1  christos 			 * transmitted if the client elects that encoding.
    721  1.1  christos 			 */
    722  1.1  christos 			memcpy(&bp->data[5], d1.data, d1.len);
    723  1.1  christos 			if (!save_option_buffer(&fqdn_universe, options,
    724  1.1  christos 						bp, &bp->data[5], d1.len,
    725  1.1  christos 						FQDN_FQDN, 0))
    726  1.1  christos 				goto badfqdn;
    727  1.1  christos 
    728  1.1  christos 			data_string_forget(&d1, MDL);
    729  1.1  christos 		}
    730  1.1  christos 	/* Set up the outgoing FQDN option if there was an incoming
    731  1.1  christos 	 * FQDN option.  If there's a valid FQDN option, there MUST
    732  1.1  christos 	 * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed
    733  1.1  christos 	 * length head of the option contents, so we test the latter
    734  1.1  christos 	 * to detect the presence of the former.
    735  1.1  christos 	 */
    736  1.1  christos 	} else if ((oc = lookup_option(&fqdn_universe, packet->options,
    737  1.1  christos 				       FQDN_ENCODED)) &&
    738  1.1  christos 		   buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
    739  1.1  christos 		bp -> data [0] = server_updates_a;
    740  1.1  christos 		if (!save_option_buffer(&fqdn_universe, options,
    741  1.1  christos 					bp, &bp->data [0], 1,
    742  1.1  christos 					FQDN_SERVER_UPDATE, 0))
    743  1.1  christos 			goto badfqdn;
    744  1.1  christos 		bp -> data [1] = server_updates_a;
    745  1.1  christos 		if (!save_option_buffer(&fqdn_universe, options,
    746  1.1  christos 					 bp, &bp->data [1], 1,
    747  1.1  christos 					 FQDN_NO_CLIENT_UPDATE, 0))
    748  1.1  christos 			goto badfqdn;
    749  1.1  christos 
    750  1.1  christos 		/* Do the same encoding the client did. */
    751  1.1  christos 		if (evaluate_boolean_option_cache(&ignorep, packet, lease,
    752  1.1  christos 						  NULL, packet->options,
    753  1.1  christos 						  options, scope, oc, MDL))
    754  1.1  christos 			bp -> data [2] = 1;
    755  1.1  christos 		else
    756  1.1  christos 			bp -> data [2] = 0;
    757  1.1  christos 		if (!save_option_buffer(&fqdn_universe, options,
    758  1.1  christos 					bp, &bp->data [2], 1,
    759  1.1  christos 					FQDN_ENCODED, 0))
    760  1.1  christos 			goto badfqdn;
    761  1.1  christos 		bp -> data [3] = 255;//isc_rcode_to_ns (rcode1);
    762  1.1  christos 		if (!save_option_buffer(&fqdn_universe, options,
    763  1.1  christos 					bp, &bp->data [3], 1,
    764  1.1  christos 					FQDN_RCODE1, 0))
    765  1.1  christos 			goto badfqdn;
    766  1.1  christos 		bp -> data [4] = 255;//isc_rcode_to_ns (rcode2);
    767  1.1  christos 		if (!save_option_buffer(&fqdn_universe, options,
    768  1.1  christos 					bp, &bp->data [4], 1,
    769  1.1  christos 					FQDN_RCODE2, 0))
    770  1.1  christos 			goto badfqdn;
    771  1.1  christos 		if (ddns_fwd_name.len) {
    772  1.1  christos 		    memcpy (&bp -> data [5],
    773  1.1  christos 			    ddns_fwd_name.data, ddns_fwd_name.len);
    774  1.1  christos 		    if (!save_option_buffer(&fqdn_universe, options,
    775  1.1  christos 					     bp, &bp->data [5],
    776  1.1  christos 					     ddns_fwd_name.len,
    777  1.1  christos 					     FQDN_FQDN, 0))
    778  1.1  christos 			goto badfqdn;
    779  1.1  christos 		}
    780  1.1  christos 	}
    781  1.1  christos 
    782  1.1  christos       badfqdn:
    783  1.1  christos       out:
    784  1.1  christos 	/*
    785  1.1  christos 	 * Final cleanup.
    786  1.1  christos 	 */
    787  1.1  christos 	if (ddns_cb != NULL) {
    788  1.1  christos 		destroy_ddns_cb(ddns_cb, MDL);
    789  1.1  christos 	}
    790  1.1  christos 
    791  1.1  christos 	data_string_forget(&d1, MDL);
    792  1.1  christos 	data_string_forget(&ddns_hostname, MDL);
    793  1.1  christos 	data_string_forget(&ddns_domainname, MDL);
    794  1.1  christos 	data_string_forget(&old_ddns_fwd_name, MDL);
    795  1.1  christos 	data_string_forget(&ddns_fwd_name, MDL);
    796  1.1  christos 	if (bp)
    797  1.1  christos 		buffer_dereference(&bp, MDL);
    798  1.1  christos 
    799  1.1  christos 	return result;
    800  1.1  christos }
    801  1.1  christos 
    802  1.1  christos /*%<
    803  1.1  christos  * Utility function to update text strings within a lease.
    804  1.1  christos  *
    805  1.1  christos  * The first issue is to find the proper scope.  Sometimes we shall be
    806  1.1  christos  * called with a pointer to the scope in other cases we need to find
    807  1.1  christos  * the proper lease and then get the scope.  Once we have the scope we update
    808  1.1  christos  * the proper strings, as indicated by the state value in the control block.
    809  1.1  christos  * Lastly, if we needed to find the scope we write it out, if we used a
    810  1.1  christos  * scope that was passed as an argument we don't write it, assuming that
    811  1.1  christos  * our caller (or his ...) will do the write.
    812  1.1  christos  *
    813  1.1  christos  *\li ddns_cb - the control block for the DDNS request
    814  1.1  christos  *
    815  1.1  christos  *\li inscope - a pointer to the scope to update.  This may be NULL
    816  1.1  christos  *    in which case we use the control block to find the lease and
    817  1.1  christos  *    then the scope.
    818  1.1  christos  *
    819  1.1  christos  * Returns
    820  1.1  christos  *\li ISC_R_SUCCESS
    821  1.1  christos  *
    822  1.1  christos  *\li ISC_R_FAILURE - The routine was unable to find an expected scope.
    823  1.1  christos  *    In some cases (static and inactive leases) we don't expect a scope
    824  1.1  christos  *    and return success.
    825  1.1  christos  */
    826  1.1  christos 
    827  1.1  christos isc_result_t
    828  1.1  christos ddns_update_lease_text(dhcp_ddns_cb_t        *ddns_cb,
    829  1.1  christos 		       struct binding_scope **inscope)
    830  1.1  christos {
    831  1.1  christos 	struct binding_scope **scope  = NULL;
    832  1.1  christos 	struct lease          *lease  = NULL;
    833  1.1  christos 	struct iasubopt       *lease6 = NULL;
    834  1.1  christos 	struct ipv6_pool      *pool   = NULL;
    835  1.1  christos 	struct in6_addr        addr;
    836  1.1  christos 	struct data_string     lease_dhcid;
    837  1.1  christos 
    838  1.1  christos 	/*
    839  1.1  christos 	 * If the lease was static (for a fixed address)
    840  1.1  christos 	 * we don't need to do any work.
    841  1.1  christos 	 */
    842  1.1  christos 	if (ddns_cb->flags & DDNS_STATIC_LEASE)
    843  1.1  christos 		return (ISC_R_SUCCESS);
    844  1.1  christos 
    845  1.1  christos 	/*
    846  1.1  christos 	 * If we are processing an expired or released v6 lease
    847  1.1  christos 	 * or some types of v4 leases we don't actually have a
    848  1.1  christos 	 * scope to update
    849  1.1  christos 	 */
    850  1.1  christos 	if ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)
    851  1.1  christos 		return (ISC_R_SUCCESS);
    852  1.1  christos 
    853  1.1  christos 	if (inscope != NULL) {
    854  1.1  christos 		scope = inscope;
    855  1.1  christos 	} else if (ddns_cb->address.len == 4) {
    856  1.1  christos 		if (find_lease_by_ip_addr(&lease, ddns_cb->address, MDL) != 0){
    857  1.1  christos 			scope = &(lease->scope);
    858  1.1  christos 		}
    859  1.1  christos 	} else if (ddns_cb->address.len == 16) {
    860  1.1  christos 		memcpy(&addr, &ddns_cb->address.iabuf, 16);
    861  1.1  christos 		if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) ==
    862  1.1  christos 		     ISC_R_SUCCESS) ||
    863  1.1  christos 		    (find_ipv6_pool(&pool, D6O_IA_NA, &addr) ==
    864  1.1  christos 		     ISC_R_SUCCESS)) {
    865  1.1  christos 			if (iasubopt_hash_lookup(&lease6,  pool->leases,
    866  1.1  christos 						 &addr, 16, MDL)) {
    867  1.1  christos 				scope = &(lease6->scope);
    868  1.1  christos 			}
    869  1.1  christos 			ipv6_pool_dereference(&pool, MDL);
    870  1.1  christos 		}
    871  1.1  christos 	} else {
    872  1.1  christos 		log_fatal("Impossible condition at %s:%d.", MDL);
    873  1.1  christos 	}
    874  1.1  christos 
    875  1.1  christos 	if (scope == NULL) {
    876  1.1  christos 		/* If necessary get rid of the lease */
    877  1.1  christos 		if (lease) {
    878  1.1  christos 			lease_dereference(&lease, MDL);
    879  1.1  christos 		}
    880  1.1  christos 		else if (lease6) {
    881  1.1  christos 			iasubopt_dereference(&lease6, MDL);
    882  1.1  christos 		}
    883  1.1  christos 
    884  1.1  christos 		return(ISC_R_FAILURE);
    885  1.1  christos 	}
    886  1.1  christos 
    887  1.1  christos 	/* We now have a scope and can proceed to update it */
    888  1.1  christos 	switch(ddns_cb->state) {
    889  1.1  christos 	case DDNS_STATE_REM_PTR:
    890  1.1  christos 		unset(*scope, "ddns-rev-name");
    891  1.1  christos 		if ((ddns_cb->flags & DDNS_CLIENT_DID_UPDATE) != 0) {
    892  1.1  christos 			unset(*scope, "ddns-client-fqdn");
    893  1.1  christos 		}
    894  1.1  christos 		break;
    895  1.1  christos 
    896  1.1  christos 	case DDNS_STATE_ADD_PTR:
    897  1.1  christos 	case DDNS_STATE_CLEANUP:
    898  1.1  christos 		bind_ds_value(scope, "ddns-rev-name", &ddns_cb->rev_name);
    899  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_ADDR) == 0) {
    900  1.1  christos 			bind_ds_value(scope, "ddns-client-fqdn",
    901  1.1  christos 				      &ddns_cb->fwd_name);
    902  1.1  christos 		}
    903  1.1  christos 		break;
    904  1.1  christos 
    905  1.1  christos 	case DDNS_STATE_ADD_FW_YXDHCID:
    906  1.1  christos 	case DDNS_STATE_ADD_FW_NXDOMAIN:
    907  1.1  christos 	case DDNS_STATE_DSMM_FW_ADD3:
    908  1.1  christos 		bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name);
    909  1.1  christos 
    910  1.1  christos 		if (ddns_cb->lease_tag == ddns_standard_tag) {
    911  1.1  christos 			bind_ds_value(scope, ddns_standard_tag,
    912  1.1  christos 				      &ddns_cb->dhcid);
    913  1.1  christos 		} else {
    914  1.1  christos 			/* convert from dns version to lease version of dhcid */
    915  1.1  christos 			memset(&lease_dhcid, 0, sizeof(lease_dhcid));
    916  1.1  christos 			dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid);
    917  1.1  christos 			bind_ds_value(scope, ddns_interim_tag, &lease_dhcid);
    918  1.1  christos 			data_string_forget(&lease_dhcid, MDL);
    919  1.1  christos 		}
    920  1.1  christos 		break;
    921  1.1  christos 
    922  1.1  christos 	case DDNS_STATE_REM_FW_NXRR:
    923  1.1  christos 	case DDNS_STATE_REM_FW_YXDHCID:
    924  1.1  christos 	case DDNS_STATE_REM_FW_DSMM_OTHER:
    925  1.1  christos 		unset(*scope, "ddns-fwd-name");
    926  1.1  christos 		unset(*scope, ddns_cb->lease_tag);
    927  1.1  christos 		break;
    928  1.1  christos 	}
    929  1.1  christos 
    930  1.1  christos 	/* If necessary write it out and get rid of the lease */
    931  1.1  christos 	if (lease) {
    932  1.1  christos 		write_lease(lease);
    933  1.1  christos 		lease_dereference(&lease, MDL);
    934  1.1  christos 	} else if (lease6) {
    935  1.1  christos 		write_ia(lease6->ia);
    936  1.1  christos 		iasubopt_dereference(&lease6, MDL);
    937  1.1  christos 	}
    938  1.1  christos 
    939  1.1  christos 	return(ISC_R_SUCCESS);
    940  1.1  christos }
    941  1.1  christos 
    942  1.1  christos /*
    943  1.1  christos  * This function should be called when update_lease_ptr function fails.
    944  1.1  christos  * It does inform user about the condition, provides some hints how to
    945  1.1  christos  * resolve this and dies gracefully. This can happend in at least three
    946  1.1  christos  * cases (all are configuration mistakes):
    947  1.1  christos  * a) IPv4: user have duplicate fixed-address entries (the same
    948  1.1  christos  *    address is defined twice). We may have found wrong lease.
    949  1.1  christos  * b) IPv6: user have overlapping pools (we tried to find
    950  1.1  christos  *    a lease in a wrong pool)
    951  1.1  christos  * c) IPv6: user have duplicate fixed-address6 entires (the same
    952  1.1  christos  *    address is defined twice). We may have found wrong lease.
    953  1.1  christos  *
    954  1.1  christos  * Comment: while it would be possible to recover from both cases
    955  1.1  christos  * by forcibly searching for leases in *all* following pools, that would
    956  1.1  christos  * only hide the real problem - a misconfiguration. Proper solution
    957  1.1  christos  * is to log the problem, die and let the user fix his config file.
    958  1.1  christos  */
    959  1.1  christos void
    960  1.1  christos update_lease_failed(struct lease *lease,
    961  1.1  christos 		    struct iasubopt *lease6,
    962  1.1  christos 		    dhcp_ddns_cb_t  *ddns_cb,
    963  1.1  christos 		    dhcp_ddns_cb_t  *ddns_cb_set,
    964  1.1  christos 		    const char * file, int line)
    965  1.1  christos {
    966  1.1  christos 	char lease_address[MAX_ADDRESS_STRING_LEN + 64];
    967  1.1  christos 	char reason[128]; /* likely reason */
    968  1.1  christos 
    969  1.1  christos 	sprintf(reason, "unknown");
    970  1.1  christos 	sprintf(lease_address, "unknown");
    971  1.1  christos 
    972  1.1  christos 	/*
    973  1.1  christos 	 * let's pretend that everything is ok, so we can continue for
    974  1.1  christos 	 * information gathering purposes
    975  1.1  christos 	 */
    976  1.1  christos 
    977  1.1  christos 	if (ddns_cb != NULL) {
    978  1.1  christos 		strncpy(lease_address, piaddr(ddns_cb->address),
    979  1.1  christos 			MAX_ADDRESS_STRING_LEN);
    980  1.1  christos 
    981  1.1  christos 		if (ddns_cb->address.len == 4) {
    982  1.1  christos 			sprintf(reason, "duplicate IPv4 fixed-address entry");
    983  1.1  christos 		} else if (ddns_cb->address.len == 16) {
    984  1.1  christos 			sprintf(reason, "duplicate IPv6 fixed-address6 entry "
    985  1.1  christos 				"or overlapping pools");
    986  1.1  christos 		} else {
    987  1.1  christos 			/*
    988  1.1  christos 			 * Should not happen. We have non-IPv4, non-IPv6
    989  1.1  christos 			 * address. Something is very wrong here.
    990  1.1  christos 			 */
    991  1.1  christos 			sprintf(reason, "corrupted ddns_cb structure (address "
    992  1.1  christos 				"length is %d)", ddns_cb->address.len);
    993  1.1  christos 		}
    994  1.1  christos 	}
    995  1.1  christos 
    996  1.1  christos 	log_error("Failed to properly update internal lease structure with "
    997  1.1  christos 		  "DDNS");
    998  1.1  christos 	log_error("control block structures. Tried to update lease for"
    999  1.1  christos 		  "%s address, ddns_cb=%p.", lease_address, ddns_cb);
   1000  1.1  christos 
   1001  1.1  christos 	log_error("%s", "");
   1002  1.1  christos 	log_error("This condition can occur, if DHCP server configuration is "
   1003  1.1  christos 		  "inconsistent.");
   1004  1.1  christos 	log_error("In particular, please do check that your configuration:");
   1005  1.1  christos 	log_error("a) does not have overlapping pools (especially containing");
   1006  1.1  christos 	log_error("   %s address).", lease_address);
   1007  1.1  christos 	log_error("b) there are no duplicate fixed-address or fixed-address6");
   1008  1.1  christos 	log_error("entries for the %s address.", lease_address);
   1009  1.1  christos 	log_error("%s", "");
   1010  1.1  christos 	log_error("Possible reason for this failure: %s", reason);
   1011  1.1  christos 
   1012  1.1  christos 	log_fatal("%s(%d): Failed to update lease database with DDNS info for "
   1013  1.1  christos 		  "address %s. Lease database inconsistent. Unable to recover."
   1014  1.1  christos 		  " Terminating.", file, line, lease_address);
   1015  1.1  christos }
   1016  1.1  christos 
   1017  1.1  christos /*
   1018  1.1  christos  * utility function to update found lease. It does extra checks
   1019  1.1  christos  * that we are indeed updating the right lease. It may happen
   1020  1.1  christos  * that user have duplicate fixed-address entries, so we attempt
   1021  1.1  christos  * to update wrong lease. See also safe_lease6_update.
   1022  1.1  christos  */
   1023  1.1  christos 
   1024  1.1  christos void
   1025  1.1  christos safe_lease_update(struct lease *lease,
   1026  1.1  christos 		  dhcp_ddns_cb_t *oldcb,
   1027  1.1  christos 		  dhcp_ddns_cb_t *newcb,
   1028  1.1  christos 		  const char *file, int line)
   1029  1.1  christos {
   1030  1.1  christos 	if (lease == NULL) {
   1031  1.1  christos 		/* should never get here */
   1032  1.1  christos 		log_fatal("Impossible condition at %s:%d (called from %s:%d).",
   1033  1.1  christos 			  MDL, file, line);
   1034  1.1  christos 	}
   1035  1.1  christos 
   1036  1.1  christos 	if ( (lease->ddns_cb == NULL) && (newcb == NULL) ) {
   1037  1.1  christos 		/*
   1038  1.1  christos 		 * Trying to clean up pointer that is already null. We
   1039  1.1  christos 		 * are most likely trying to update wrong lease here.
   1040  1.1  christos 		 */
   1041  1.1  christos 
   1042  1.1  christos 		/*
   1043  1.1  christos 		 * Previously this error message popped out during
   1044  1.1  christos 		 * DNS update for fixed leases.  As we no longer
   1045  1.1  christos 		 * try to update the lease for a fixed (static) lease
   1046  1.1  christos 		 * this should not be a problem.
   1047  1.1  christos 		 */
   1048  1.1  christos 		log_error("%s(%d): Invalid lease update. Tried to "
   1049  1.1  christos 			  "clear already NULL DDNS control block "
   1050  1.1  christos 			  "pointer for lease %s.",
   1051  1.1  christos 			  file, line, piaddr(lease->ip_addr) );
   1052  1.1  christos 
   1053  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1054  1.1  christos 		update_lease_failed(lease, NULL, oldcb, newcb, file, line);
   1055  1.1  christos #endif
   1056  1.1  christos 		/*
   1057  1.1  christos 		 * May not reach this: update_lease_failed calls
   1058  1.1  christos 		 * log_fatal.
   1059  1.1  christos 		 */
   1060  1.1  christos 		return;
   1061  1.1  christos 	}
   1062  1.1  christos 
   1063  1.1  christos 	if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) {
   1064  1.1  christos 		/*
   1065  1.1  christos 		 * There is existing cb structure, but it differs from
   1066  1.1  christos 		 * what we expected to see there. Most likely we are
   1067  1.1  christos 		 * trying to update wrong lease.
   1068  1.1  christos 		 */
   1069  1.1  christos 		log_error("%s(%d): Failed to update internal lease "
   1070  1.1  christos 			  "structure with DDNS control block. Existing"
   1071  1.1  christos 			  " ddns_cb structure does not match "
   1072  1.1  christos 			  "expectations.IPv4=%s, old ddns_cb=%p, tried"
   1073  1.1  christos 			  "to update to new ddns_cb=%p", file, line,
   1074  1.1  christos 			  piaddr(lease->ip_addr), oldcb,  newcb);
   1075  1.1  christos 
   1076  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1077  1.1  christos 		update_lease_failed(lease, NULL, oldcb, newcb, file, line);
   1078  1.1  christos #endif
   1079  1.1  christos 		/*
   1080  1.1  christos 		 * May not reach this: update_lease_failed calls
   1081  1.1  christos 		 * log_fatal.
   1082  1.1  christos 		 */
   1083  1.1  christos 		return;
   1084  1.1  christos 	}
   1085  1.1  christos 
   1086  1.1  christos 	/* additional IPv4 specific checks may be added here */
   1087  1.1  christos 
   1088  1.1  christos 	/* update the lease */
   1089  1.1  christos 	lease->ddns_cb = newcb;
   1090  1.1  christos }
   1091  1.1  christos 
   1092  1.1  christos void
   1093  1.1  christos safe_lease6_update(struct iasubopt *lease6,
   1094  1.1  christos 		   dhcp_ddns_cb_t *oldcb,
   1095  1.1  christos 		   dhcp_ddns_cb_t *newcb,
   1096  1.1  christos 		   const char *file, int line)
   1097  1.1  christos {
   1098  1.1  christos 	char addrbuf[MAX_ADDRESS_STRING_LEN];
   1099  1.1  christos 
   1100  1.1  christos 	if (lease6 == NULL) {
   1101  1.1  christos 		/* should never get here */
   1102  1.1  christos 		log_fatal("Impossible condition at %s:%d (called from %s:%d).",
   1103  1.1  christos 			  MDL, file, line);
   1104  1.1  christos 	}
   1105  1.1  christos 
   1106  1.1  christos 	if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) {
   1107  1.1  christos 		inet_ntop(AF_INET6, &lease6->addr, addrbuf,
   1108  1.1  christos 			  MAX_ADDRESS_STRING_LEN);
   1109  1.1  christos 		/*
   1110  1.1  christos 		 * Trying to clean up pointer that is already null. We
   1111  1.1  christos 		 * are most likely trying to update wrong lease here.
   1112  1.1  christos 		 */
   1113  1.1  christos 		log_error("%s(%d): Failed to update internal lease "
   1114  1.1  christos 			  "structure. Tried to clear already NULL "
   1115  1.1  christos 			  "DDNS control block pointer for lease %s.",
   1116  1.1  christos 			  file, line, addrbuf);
   1117  1.1  christos 
   1118  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1119  1.1  christos 		update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
   1120  1.1  christos #endif
   1121  1.1  christos 
   1122  1.1  christos 		/*
   1123  1.1  christos 		 * May not reach this: update_lease_failed calls
   1124  1.1  christos 		 * log_fatal.
   1125  1.1  christos 		 */
   1126  1.1  christos 		return;
   1127  1.1  christos 	}
   1128  1.1  christos 
   1129  1.1  christos 	if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) {
   1130  1.1  christos 		/*
   1131  1.1  christos 		 * there is existing cb structure, but it differs from
   1132  1.1  christos 		 * what we expected to see there. Most likely we are
   1133  1.1  christos 		 * trying to update wrong lease.
   1134  1.1  christos 		 */
   1135  1.1  christos 		inet_ntop(AF_INET6, &lease6->addr, addrbuf,
   1136  1.1  christos 			  MAX_ADDRESS_STRING_LEN);
   1137  1.1  christos 
   1138  1.1  christos 		log_error("%s(%d): Failed to update internal lease "
   1139  1.1  christos 			  "structure with DDNS control block. Existing"
   1140  1.1  christos 			  " ddns_cb structure does not match "
   1141  1.1  christos 			  "expectations.IPv6=%s, old ddns_cb=%p, tried"
   1142  1.1  christos 			  "to update to new ddns_cb=%p", file, line,
   1143  1.1  christos 			  addrbuf, oldcb,  newcb);
   1144  1.1  christos 
   1145  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1146  1.1  christos 		update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
   1147  1.1  christos #endif
   1148  1.1  christos 		/*
   1149  1.1  christos 		 * May not reach this: update_lease_failed calls
   1150  1.1  christos 		 * log_fatal.
   1151  1.1  christos 		 */
   1152  1.1  christos 		return;
   1153  1.1  christos 	}
   1154  1.1  christos 	/* additional IPv6 specific checks may be added here */
   1155  1.1  christos 
   1156  1.1  christos 	/* update the lease */
   1157  1.1  christos 	lease6->ddns_cb = newcb;
   1158  1.1  christos }
   1159  1.1  christos 
   1160  1.1  christos /*
   1161  1.1  christos  * Utility function to update the pointer to the DDNS control block
   1162  1.1  christos  * in a lease.
   1163  1.1  christos  * SUCCESS - able to update the pointer
   1164  1.1  christos  * FAILURE - lease didn't exist or sanity checks failed
   1165  1.1  christos  * lease and lease6 may be empty in which case we attempt to find
   1166  1.1  christos  * the lease from the ddns_cb information.
   1167  1.1  christos  * ddns_cb is the control block to use if a lookup is necessary
   1168  1.1  christos  * ddns_cb_set is the pointer to insert into the lease and may be NULL
   1169  1.1  christos  * The last two arguments may look odd as they will be the same much of the
   1170  1.1  christos  * time, but I need an argument to tell me if I'm setting or clearing in
   1171  1.1  christos  * addition to the address information from the cb to look up the lease.
   1172  1.1  christos  * using the same value twice allows me more flexibility.
   1173  1.1  christos  */
   1174  1.1  christos 
   1175  1.1  christos isc_result_t
   1176  1.1  christos ddns_update_lease_ptr(struct lease    *lease,
   1177  1.1  christos 		      struct iasubopt *lease6,
   1178  1.1  christos 		      dhcp_ddns_cb_t  *ddns_cb,
   1179  1.1  christos 		      dhcp_ddns_cb_t  *ddns_cb_set,
   1180  1.1  christos 		      const char * file, int line)
   1181  1.1  christos {
   1182  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1183  1.1  christos 	sprintf(ddns_address, "unknown");
   1184  1.1  christos 	if (ddns_cb == NULL) {
   1185  1.1  christos 		log_info("%s(%d): No control block for lease update",
   1186  1.1  christos 			 file, line);
   1187  1.1  christos 		return (ISC_R_FAILURE);
   1188  1.1  christos 	}
   1189  1.1  christos 	else {
   1190  1.1  christos 		strcpy(ddns_address, piaddr(ddns_cb->address));
   1191  1.1  christos 	}
   1192  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1193  1.1  christos 	log_info("%s(%d): Updating lease_ptr for ddns_cp=%p (addr=%s)",
   1194  1.1  christos 		 file, line, ddns_cb, ddns_address );
   1195  1.1  christos #endif
   1196  1.1  christos 
   1197  1.1  christos 	/*
   1198  1.1  christos 	 * If the lease was static (for a fixed address)
   1199  1.1  christos 	 * we don't need to do any work.
   1200  1.1  christos 	 */
   1201  1.1  christos 	if (ddns_cb->flags & DDNS_STATIC_LEASE) {
   1202  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1203  1.1  christos 		log_info("lease is static, returning");
   1204  1.1  christos #endif
   1205  1.1  christos 		return (ISC_R_SUCCESS);
   1206  1.1  christos 	}
   1207  1.1  christos 
   1208  1.1  christos 	/*
   1209  1.1  christos 	 * If we are processing an expired or released v6 lease
   1210  1.1  christos 	 * we don't actually have a lease to update
   1211  1.1  christos 	 */
   1212  1.1  christos 	if ((ddns_cb->address.len == 16) &&
   1213  1.1  christos 	    ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)) {
   1214  1.1  christos 		return (ISC_R_SUCCESS);
   1215  1.1  christos 	}
   1216  1.1  christos 
   1217  1.1  christos 	if (lease != NULL) {
   1218  1.1  christos 		safe_lease_update(lease, ddns_cb, ddns_cb_set,
   1219  1.1  christos 				  file, line);
   1220  1.1  christos 	} else if (lease6 != NULL) {
   1221  1.1  christos 		safe_lease6_update(lease6, ddns_cb, ddns_cb_set,
   1222  1.1  christos 				  file, line);
   1223  1.1  christos 	} else if (ddns_cb->address.len == 4) {
   1224  1.1  christos 		struct lease *find_lease = NULL;
   1225  1.1  christos 		if (find_lease_by_ip_addr(&find_lease,
   1226  1.1  christos 					  ddns_cb->address, MDL) != 0) {
   1227  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1228  1.1  christos 			log_info("%s(%d): find_lease_by_ip_addr(%s) successful:"
   1229  1.1  christos 				 "lease=%p", file, line, ddns_address,
   1230  1.1  christos 				 find_lease);
   1231  1.1  christos #endif
   1232  1.1  christos 
   1233  1.1  christos 			safe_lease_update(find_lease, ddns_cb,
   1234  1.1  christos 					  ddns_cb_set, file, line);
   1235  1.1  christos 			lease_dereference(&find_lease, MDL);
   1236  1.1  christos 		}
   1237  1.1  christos 		else {
   1238  1.1  christos 			log_error("%s(%d): ddns_update_lease_ptr failed. "
   1239  1.1  christos 				  "Lease for %s not found.",
   1240  1.1  christos 				  file, line, piaddr(ddns_cb->address));
   1241  1.1  christos 
   1242  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1243  1.1  christos 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
   1244  1.1  christos 					    file, line);
   1245  1.1  christos #endif
   1246  1.1  christos 			/*
   1247  1.1  christos 			 * may not reach this. update_lease_failed
   1248  1.1  christos 			 * calls log_fatal.
   1249  1.1  christos 			 */
   1250  1.1  christos 			return(ISC_R_FAILURE);
   1251  1.1  christos 
   1252  1.1  christos 		}
   1253  1.1  christos 	} else if (ddns_cb->address.len == 16) {
   1254  1.1  christos 		struct iasubopt *find_lease6 = NULL;
   1255  1.1  christos 		struct ipv6_pool *pool = NULL;
   1256  1.1  christos 		struct in6_addr addr;
   1257  1.1  christos 		char addrbuf[MAX_ADDRESS_STRING_LEN];
   1258  1.1  christos 
   1259  1.1  christos 		memcpy(&addr, &ddns_cb->address.iabuf, 16);
   1260  1.1  christos 		if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) !=
   1261  1.1  christos 		     ISC_R_SUCCESS) &&
   1262  1.1  christos 		    (find_ipv6_pool(&pool, D6O_IA_NA, &addr) !=
   1263  1.1  christos 		     ISC_R_SUCCESS)) {
   1264  1.1  christos 			inet_ntop(AF_INET6, &addr, addrbuf,
   1265  1.1  christos 				  MAX_ADDRESS_STRING_LEN);
   1266  1.1  christos 			log_error("%s(%d): Pool for lease %s not found.",
   1267  1.1  christos 				  file, line, addrbuf);
   1268  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1269  1.1  christos 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
   1270  1.1  christos 					    file, line);
   1271  1.1  christos #endif
   1272  1.1  christos 			/*
   1273  1.1  christos 			 * never reached. update_lease_failed
   1274  1.1  christos 			 * calls log_fatal.
   1275  1.1  christos 			 */
   1276  1.1  christos 			return(ISC_R_FAILURE);
   1277  1.1  christos 		}
   1278  1.1  christos 
   1279  1.1  christos 		if (iasubopt_hash_lookup(&find_lease6, pool->leases,
   1280  1.1  christos 					 &addr, 16, MDL)) {
   1281  1.1  christos 			find_lease6->ddns_cb = ddns_cb_set;
   1282  1.1  christos 			iasubopt_dereference(&find_lease6, MDL);
   1283  1.1  christos 		} else {
   1284  1.1  christos 			inet_ntop(AF_INET6, &addr, addrbuf,
   1285  1.1  christos 				  MAX_ADDRESS_STRING_LEN);
   1286  1.1  christos 			log_error("%s(%d): Lease %s not found within pool.",
   1287  1.1  christos 				  file, line, addrbuf);
   1288  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1289  1.1  christos 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
   1290  1.1  christos 					    file, line);
   1291  1.1  christos #endif
   1292  1.1  christos 			/*
   1293  1.1  christos 			 * never reached. update_lease_failed
   1294  1.1  christos 			 * calls log_fatal.
   1295  1.1  christos 			 */
   1296  1.1  christos 			return(ISC_R_FAILURE);
   1297  1.1  christos 		}
   1298  1.1  christos 		ipv6_pool_dereference(&pool, MDL);
   1299  1.1  christos 	} else {
   1300  1.1  christos 		/* shouldn't get here */
   1301  1.1  christos 		log_fatal("Impossible condition at %s:%d, called from %s:%d.",
   1302  1.1  christos 			  MDL, file, line);
   1303  1.1  christos 	}
   1304  1.1  christos 
   1305  1.1  christos 	return(ISC_R_SUCCESS);
   1306  1.1  christos }
   1307  1.1  christos 
   1308  1.1  christos void
   1309  1.1  christos ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb,
   1310  1.1  christos 	     isc_result_t    eresult)
   1311  1.1  christos {
   1312  1.1  christos 	if (eresult == ISC_R_SUCCESS) {
   1313  1.1  christos 		log_info("Added reverse map from %.*s to %.*s",
   1314  1.1  christos 			 (int)ddns_cb->rev_name.len,
   1315  1.1  christos 			 (const char *)ddns_cb->rev_name.data,
   1316  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1317  1.1  christos 			 (const char *)ddns_cb->fwd_name.data);
   1318  1.1  christos 
   1319  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1320  1.1  christos 	} else {
   1321  1.1  christos 		log_error("Unable to add reverse map from %.*s to %.*s: %s",
   1322  1.1  christos 			  (int)ddns_cb->rev_name.len,
   1323  1.1  christos 			  (const char *)ddns_cb->rev_name.data,
   1324  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1325  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1326  1.1  christos 			  isc_result_totext (eresult));
   1327  1.1  christos 	}
   1328  1.1  christos 
   1329  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1330  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1331  1.1  christos 	/*
   1332  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1333  1.1  christos 	 * the current state as the prerequisites for the first message
   1334  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1335  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1336  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1337  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1338  1.1  christos 	 * here rather than immediately after the call to update the lease
   1339  1.1  christos 	 * text in order to save any previously written data.
   1340  1.1  christos 	 */
   1341  1.1  christos 	commit_leases();
   1342  1.1  christos 	return;
   1343  1.1  christos }
   1344  1.1  christos 
   1345  1.1  christos /*
   1346  1.1  christos  * action routine when trying to remove a pointer
   1347  1.1  christos  * this will be called after the ddns queries have completed
   1348  1.1  christos  * if we succeeded in removing the pointer we go to the next step (if any)
   1349  1.1  christos  * if not we cleanup and leave.
   1350  1.1  christos  */
   1351  1.1  christos 
   1352  1.1  christos void
   1353  1.1  christos ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb,
   1354  1.1  christos 		isc_result_t    eresult)
   1355  1.1  christos {
   1356  1.1  christos 	isc_result_t result = eresult;
   1357  1.1  christos 
   1358  1.1  christos 	switch(eresult) {
   1359  1.1  christos 	case ISC_R_SUCCESS:
   1360  1.1  christos 		log_info("Removed reverse map on %.*s",
   1361  1.1  christos 			 (int)ddns_cb->rev_name.len,
   1362  1.1  christos 			 (const char *)ddns_cb->rev_name.data);
   1363  1.1  christos 		/* fall through */
   1364  1.1  christos 	case DNS_R_NXRRSET:
   1365  1.1  christos 	case DNS_R_NXDOMAIN:
   1366  1.1  christos 		/* No entry is the same as success.
   1367  1.1  christos 		 * Remove the information from the lease and
   1368  1.1  christos 		 * continue with any next step */
   1369  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1370  1.1  christos 
   1371  1.1  christos 		/* trigger any add operation */
   1372  1.1  christos 		result = ISC_R_SUCCESS;
   1373  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1374  1.1  christos 		log_info("DDNS: removed map or no reverse map to remove %.*s",
   1375  1.1  christos 			 (int)ddns_cb->rev_name.len,
   1376  1.1  christos 			 (const char *)ddns_cb->rev_name.data);
   1377  1.1  christos #endif
   1378  1.1  christos 		break;
   1379  1.1  christos 
   1380  1.1  christos 	default:
   1381  1.1  christos 		log_error("Can't remove reverse map on %.*s: %s",
   1382  1.1  christos 			  (int)ddns_cb->rev_name.len,
   1383  1.1  christos 			  (const char *)ddns_cb->rev_name.data,
   1384  1.1  christos 			  isc_result_totext (eresult));
   1385  1.1  christos 		break;
   1386  1.1  christos 	}
   1387  1.1  christos 
   1388  1.1  christos 	/* If we aren't suppossed to do the next step, set the result
   1389  1.1  christos 	 * flag so ddns_fwd_srv_connector won't do much
   1390  1.1  christos 	 */
   1391  1.1  christos 	if ((ddns_cb->flags & DDNS_EXECUTE_NEXT) == 0)
   1392  1.1  christos 		result = ISC_R_FAILURE;
   1393  1.1  christos 
   1394  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1395  1.1  christos 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result);
   1396  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1397  1.1  christos 	return;
   1398  1.1  christos }
   1399  1.1  christos 
   1400  1.1  christos 
   1401  1.1  christos /*
   1402  1.1  christos  * If the first query succeeds, the updater can conclude that it
   1403  1.1  christos  * has added a new name whose only RRs are the A and DHCID RR records.
   1404  1.1  christos  * The A RR update is now complete (and a client updater is finished,
   1405  1.1  christos  * while a server might proceed to perform a PTR RR update).
   1406  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1407  1.1  christos  *
   1408  1.1  christos  * If the second query succeeds, the updater can conclude that the current
   1409  1.1  christos  * client was the last client associated with the domain name, and that
   1410  1.1  christos  * the name now contains the updated A RR. The A RR update is now
   1411  1.1  christos  * complete (and a client updater is finished, while a server would
   1412  1.1  christos  * then proceed to perform a PTR RR update).
   1413  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1414  1.1  christos  *
   1415  1.1  christos  * If the second query fails with NXRRSET, the updater must conclude
   1416  1.1  christos  * that the client's desired name is in use by another host.  If
   1417  1.1  christos  * Dual Stack Mixed Mode (DSMM) is enabled and we proceed to a
   1418  1.1  christos  * third stage forward update attempt specific to DSMM rules.  If not,
   1419  1.1  christos  * then the existing entries are left intact:
   1420  1.1  christos  *
   1421  1.1  christos  * At this juncture, the updater can decide (based on some administrative
   1422  1.1  christos  * configuration outside of the scope of this document) whether to let
   1423  1.1  christos  * the existing owner of the name keep that name, and to (possibly)
   1424  1.1  christos  * perform some name disambiguation operation on behalf of the current
   1425  1.1  christos  * client, or to replace the RRs on the name with RRs that represent
   1426  1.1  christos  * the current client. If the configured policy allows replacement of
   1427  1.1  christos  * existing records, the updater submits a query that deletes the
   1428  1.1  christos  * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
   1429  1.1  christos  * represent the IP address and client-identity of the new client.
   1430  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1431  1.1  christos  */
   1432  1.1  christos 
   1433  1.1  christos void
   1434  1.1  christos ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb,
   1435  1.1  christos 		  isc_result_t    eresult)
   1436  1.1  christos {
   1437  1.1  christos 	isc_result_t result;
   1438  1.1  christos 	const char *logstr = NULL;
   1439  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1440  1.1  christos 
   1441  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1442  1.1  christos 	log_info ("DDNS:ddns_fwd_srv_add2: %s eresult: %d",
   1443  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1444  1.1  christos #endif
   1445  1.1  christos 
   1446  1.1  christos 	/* Construct a printable form of the address for logging */
   1447  1.1  christos 	strcpy(ddns_address, piaddr(ddns_cb->address));
   1448  1.1  christos 
   1449  1.1  christos 	switch(eresult) {
   1450  1.1  christos 	case ISC_R_SUCCESS:
   1451  1.1  christos 		log_info("Added new forward map from %.*s to %s",
   1452  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1453  1.1  christos 			 (const char *)ddns_cb->fwd_name.data,
   1454  1.1  christos 			 ddns_address);
   1455  1.1  christos 
   1456  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1457  1.1  christos 
   1458  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1459  1.1  christos 			/* if we have zone information get rid of it */
   1460  1.1  christos 			if (ddns_cb->zone != NULL) {
   1461  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1462  1.1  christos 			}
   1463  1.1  christos 
   1464  1.1  christos 			ddns_cb->state = DDNS_STATE_ADD_PTR;
   1465  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1466  1.1  christos 
   1467  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1468  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1469  1.1  christos 				return;
   1470  1.1  christos 			}
   1471  1.1  christos 		}
   1472  1.1  christos 		break;
   1473  1.1  christos 
   1474  1.1  christos 	case DNS_R_YXRRSET:
   1475  1.1  christos 	case DNS_R_YXDOMAIN:
   1476  1.1  christos 		logstr = "DHCID mismatch, belongs to another client.";
   1477  1.1  christos 		break;
   1478  1.1  christos 
   1479  1.1  christos 	case DNS_R_NXDOMAIN:
   1480  1.1  christos 	case DNS_R_NXRRSET:
   1481  1.1  christos 		/* If DSMM is on we need to try forward add3 */
   1482  1.1  christos 		if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
   1483  1.1  christos 			ddns_cb->state = DDNS_STATE_DSMM_FW_ADD3;
   1484  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_add3;
   1485  1.1  christos 
   1486  1.1  christos 			result = ddns_modify_fwd(ddns_cb, MDL);
   1487  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1488  1.1  christos 				return;
   1489  1.1  christos 			}
   1490  1.1  christos 
   1491  1.1  christos 			break;
   1492  1.1  christos 		}
   1493  1.1  christos 
   1494  1.1  christos 		logstr = "Has an address record but no DHCID, not mine.";
   1495  1.1  christos 		break;
   1496  1.1  christos 
   1497  1.1  christos 	default:
   1498  1.1  christos 		logstr = isc_result_totext(eresult);
   1499  1.1  christos 		break;
   1500  1.1  christos 	}
   1501  1.1  christos 
   1502  1.1  christos 	if (logstr != NULL) {
   1503  1.1  christos 		log_error("Forward map from %.*s to %s FAILED: %s",
   1504  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1505  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1506  1.1  christos 			  ddns_address, logstr);
   1507  1.1  christos 	}
   1508  1.1  christos 
   1509  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1510  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1511  1.1  christos 	/*
   1512  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1513  1.1  christos 	 * the current state as the prerequisites for the first message
   1514  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1515  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1516  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1517  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1518  1.1  christos 	 * here rather than immediately after the call to update the lease
   1519  1.1  christos 	 * text in order to save any previously written data.
   1520  1.1  christos 	 */
   1521  1.1  christos 	commit_leases();
   1522  1.1  christos 	return;
   1523  1.1  christos }
   1524  1.1  christos 
   1525  1.1  christos void
   1526  1.1  christos ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb,
   1527  1.1  christos 		  isc_result_t    eresult)
   1528  1.1  christos {
   1529  1.1  christos 	isc_result_t result;
   1530  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1531  1.1  christos 
   1532  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1533  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_add1: %s eresult: %d",
   1534  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1535  1.1  christos #endif
   1536  1.1  christos 
   1537  1.1  christos 	/* Construct a printable form of the address for logging */
   1538  1.1  christos 	strcpy(ddns_address, piaddr(ddns_cb->address));
   1539  1.1  christos 
   1540  1.1  christos 	switch(eresult) {
   1541  1.1  christos 	case ISC_R_SUCCESS:
   1542  1.1  christos 		log_info ("Added new forward map from %.*s to %s",
   1543  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1544  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1545  1.1  christos 			  ddns_address);
   1546  1.1  christos 
   1547  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1548  1.1  christos 
   1549  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1550  1.1  christos 			/* if we have zone information get rid of it */
   1551  1.1  christos 			if (ddns_cb->zone != NULL) {
   1552  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1553  1.1  christos 			}
   1554  1.1  christos 
   1555  1.1  christos 			ddns_cb->state = DDNS_STATE_ADD_PTR;
   1556  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1557  1.1  christos 
   1558  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1559  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1560  1.1  christos 				return;
   1561  1.1  christos 			}
   1562  1.1  christos 		}
   1563  1.1  christos 		break;
   1564  1.1  christos 
   1565  1.1  christos 	case DNS_R_YXDOMAIN:
   1566  1.1  christos 		/* we can reuse the zone information */
   1567  1.1  christos 		ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
   1568  1.1  christos 		ddns_cb->cur_func = ddns_fwd_srv_add2;
   1569  1.1  christos 
   1570  1.1  christos 		result = ddns_modify_fwd(ddns_cb, MDL);
   1571  1.1  christos 		if (result == ISC_R_SUCCESS) {
   1572  1.1  christos 			return;
   1573  1.1  christos 		}
   1574  1.1  christos 		break;
   1575  1.1  christos 	default:
   1576  1.1  christos 		log_error ("Unable to add forward map from %.*s to %s: %s",
   1577  1.1  christos 			   (int)ddns_cb->fwd_name.len,
   1578  1.1  christos 			   (const char *)ddns_cb->fwd_name.data,
   1579  1.1  christos 			   ddns_address,
   1580  1.1  christos 			   isc_result_totext (eresult));
   1581  1.1  christos 		break;
   1582  1.1  christos 	}
   1583  1.1  christos 
   1584  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1585  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1586  1.1  christos 	/*
   1587  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1588  1.1  christos 	 * the current state as the prerequisites for the first message
   1589  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1590  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1591  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1592  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1593  1.1  christos 	 * here rather than immediately after the call to update the lease
   1594  1.1  christos 	 * text in order to save any previously written data.
   1595  1.1  christos 	 */
   1596  1.1  christos 	commit_leases();
   1597  1.1  christos 	return;
   1598  1.1  christos }
   1599  1.1  christos 
   1600  1.1  christos /*
   1601  1.1  christos  * This action routine is invoked after the DSMM third add stage is
   1602  1.1  christos  * attempted.  If we succeeded we attempt to update the reverse DNS,
   1603  1.1  christos  * if not we cleanup and leave.
   1604  1.1  christos  */
   1605  1.1  christos void
   1606  1.1  christos ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb,
   1607  1.1  christos 		  isc_result_t    eresult)
   1608  1.1  christos {
   1609  1.1  christos 	isc_result_t result;
   1610  1.1  christos 	const char *logstr = NULL;
   1611  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN+1];
   1612  1.1  christos 
   1613  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1614  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_add3: %s eresult: %d",
   1615  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1616  1.1  christos #endif
   1617  1.1  christos 
   1618  1.1  christos 	/* Construct a printable form of the address for logging */
   1619  1.1  christos 	memset(ddns_address, 0x0, sizeof(ddns_address));
   1620  1.1  christos 	strncpy(ddns_address, piaddr(ddns_cb->address),
   1621  1.1  christos                 sizeof(ddns_address) - 1);
   1622  1.1  christos 
   1623  1.1  christos 	switch(eresult) {
   1624  1.1  christos 	case ISC_R_SUCCESS:
   1625  1.1  christos 		log_info("Added new forward map from %.*s to %s",
   1626  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1627  1.1  christos 			 (const char *)ddns_cb->fwd_name.data,
   1628  1.1  christos 			 ddns_address);
   1629  1.1  christos 
   1630  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1631  1.1  christos 
   1632  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1633  1.1  christos 			/* if we have zone information get rid of it */
   1634  1.1  christos 			if (ddns_cb->zone != NULL) {
   1635  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1636  1.1  christos 			}
   1637  1.1  christos 
   1638  1.1  christos 			ddns_cb->state = DDNS_STATE_ADD_PTR;
   1639  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1640  1.1  christos 
   1641  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1642  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1643  1.1  christos 				return;
   1644  1.1  christos 			}
   1645  1.1  christos 		}
   1646  1.1  christos 		break;
   1647  1.1  christos 
   1648  1.1  christos 	case DNS_R_YXRRSET:
   1649  1.1  christos 		logstr = "an entry that is either static or "
   1650  1.1  christos 			 "owned by another client exists.";
   1651  1.1  christos 		break;
   1652  1.1  christos 
   1653  1.1  christos 	case DNS_R_NXRRSET:
   1654  1.1  christos 		logstr = "static entry of the other protocol type exists.";
   1655  1.1  christos 		break;
   1656  1.1  christos 
   1657  1.1  christos 	default:
   1658  1.1  christos 		logstr = isc_result_totext(eresult);
   1659  1.1  christos 		break;
   1660  1.1  christos 	}
   1661  1.1  christos 
   1662  1.1  christos 	if (logstr != NULL) {
   1663  1.1  christos 		log_error("Forward map from %.*s to %s FAILED: %s",
   1664  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1665  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1666  1.1  christos 			  ddns_address, logstr);
   1667  1.1  christos 	}
   1668  1.1  christos 
   1669  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1670  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1671  1.1  christos 	/*
   1672  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1673  1.1  christos 	 * the current state as the prerequisites for the first message
   1674  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1675  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1676  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1677  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1678  1.1  christos 	 * here rather than immediately after the call to update the lease
   1679  1.1  christos 	 * text in order to save any previously written data.
   1680  1.1  christos 	 */
   1681  1.1  christos 	commit_leases();
   1682  1.1  christos 	return;
   1683  1.1  christos }
   1684  1.1  christos 
   1685  1.1  christos static void
   1686  1.1  christos ddns_fwd_srv_connector(struct lease          *lease,
   1687  1.1  christos 		       struct iasubopt       *lease6,
   1688  1.1  christos 		       struct binding_scope **inscope,
   1689  1.1  christos 		       dhcp_ddns_cb_t        *ddns_cb,
   1690  1.1  christos 		       isc_result_t           eresult)
   1691  1.1  christos {
   1692  1.1  christos 	isc_result_t result = ISC_R_FAILURE;
   1693  1.1  christos 
   1694  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1695  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_connector: %s eresult: %d",
   1696  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1697  1.1  christos #endif
   1698  1.1  christos 
   1699  1.1  christos 	if (ddns_cb == NULL) {
   1700  1.1  christos 		/* nothing to do */
   1701  1.1  christos 		return;
   1702  1.1  christos 	}
   1703  1.1  christos 
   1704  1.1  christos 	if (eresult == ISC_R_SUCCESS) {
   1705  1.1  christos 		/*
   1706  1.1  christos 		 * If we have updates dispatch as appropriate,
   1707  1.1  christos 		 * if not do FQDN binding if desired.
   1708  1.1  christos 		 */
   1709  1.1  christos 
   1710  1.1  christos 		if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
   1711  1.1  christos 			ddns_cb->state    = DDNS_STATE_ADD_FW_NXDOMAIN;
   1712  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_add1;
   1713  1.1  christos 			result = ddns_modify_fwd(ddns_cb, MDL);
   1714  1.1  christos 		} else if ((ddns_cb->flags & DDNS_UPDATE_PTR) &&
   1715  1.1  christos 			 (ddns_cb->rev_name.len != 0)) {
   1716  1.1  christos 			ddns_cb->state    = DDNS_STATE_ADD_PTR;
   1717  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1718  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1719  1.1  christos 		} else {
   1720  1.1  christos 			ddns_update_lease_text(ddns_cb, inscope);
   1721  1.1  christos 		}
   1722  1.1  christos 	}
   1723  1.1  christos 
   1724  1.1  christos 
   1725  1.1  christos 	if (result == ISC_R_SUCCESS) {
   1726  1.1  christos 		ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL);
   1727  1.1  christos 	} else {
   1728  1.1  christos 		destroy_ddns_cb(ddns_cb, MDL);
   1729  1.1  christos 	}
   1730  1.1  christos 
   1731  1.1  christos 	return;
   1732  1.1  christos }
   1733  1.1  christos 
   1734  1.1  christos /*
   1735  1.1  christos  * If the first query fails, the updater MUST NOT delete the DNS name.  It
   1736  1.1  christos  * may be that the host whose lease on the server has expired has moved
   1737  1.1  christos  * to another network and obtained a lease from a different server,
   1738  1.1  christos  * which has caused the client's A RR to be replaced. It may also be
   1739  1.1  christos  * that some other client has been configured with a name that matches
   1740  1.1  christos  * the name of the DHCP client, and the policy was that the last client
   1741  1.1  christos  * to specify the name would get the name.  In this case, the DHCID RR
   1742  1.1  christos  * will no longer match the updater's notion of the client-identity of
   1743  1.1  christos  * the host pointed to by the DNS name.
   1744  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1745  1.1  christos  */
   1746  1.1  christos 
   1747  1.1  christos void
   1748  1.1  christos ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb,
   1749  1.1  christos 		  isc_result_t    eresult)
   1750  1.1  christos {
   1751  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1752  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_rem2: %s eresult: %d",
   1753  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1754  1.1  christos #endif
   1755  1.1  christos 
   1756  1.1  christos 	/*
   1757  1.1  christos 	 * To get here we have already managed to remove the A/AAAA
   1758  1.1  christos 	 * record and are trying to remove the DHCID/TXT record as well.
   1759  1.1  christos 	 * On success (removed DHCID/TXT) or YXRRSET (DHCID/TXT still in
   1760  1.1  christos 	 * use by something else) we clean up the lease.
   1761  1.1  christos 	 * On some other error we don't clean up the lease and hope that
   1762  1.1  christos 	 * if we try this again it will work.  An example would be if we
   1763  1.1  christos 	 * got a timeout as the DNS server halted between the first and
   1764  1.1  christos 	 * second steps.  The DNS server would still have the DHCID/TXT
   1765  1.1  christos 	 * and we would like to remove that in the future.
   1766  1.1  christos 	 *
   1767  1.1  christos 	 * On success set the EXECUTE_NEXT flag which triggers any
   1768  1.1  christos 	 * add that is next in the chain.
   1769  1.1  christos 	 */
   1770  1.1  christos 	if ((eresult == ISC_R_SUCCESS) ||
   1771  1.1  christos 	    (eresult == DNS_R_YXRRSET))  {
   1772  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1773  1.1  christos 		eresult = ISC_R_SUCCESS;
   1774  1.1  christos 	}
   1775  1.1  christos 
   1776  1.1  christos 	/* Do the next operation */
   1777  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1778  1.1  christos 		/* if we have zone information get rid of it */
   1779  1.1  christos 		if (ddns_cb->zone != NULL) {
   1780  1.1  christos 			ddns_cb_forget_zone(ddns_cb);
   1781  1.1  christos 		}
   1782  1.1  christos 
   1783  1.1  christos 		ddns_cb->state = DDNS_STATE_REM_PTR;
   1784  1.1  christos 		ddns_cb->cur_func = ddns_ptr_remove;
   1785  1.1  christos 		if (eresult == ISC_R_SUCCESS)
   1786  1.1  christos 			ddns_cb->flags |= DDNS_EXECUTE_NEXT;
   1787  1.1  christos 
   1788  1.1  christos 		eresult = ddns_modify_ptr(ddns_cb, MDL);
   1789  1.1  christos 		if (eresult == ISC_R_SUCCESS) {
   1790  1.1  christos 			return;
   1791  1.1  christos 		}
   1792  1.1  christos 	}
   1793  1.1  christos 
   1794  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1795  1.1  christos 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
   1796  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1797  1.1  christos 	return;
   1798  1.1  christos }
   1799  1.1  christos 
   1800  1.1  christos 
   1801  1.1  christos /*
   1802  1.1  christos  * First action routine when trying to remove a fwd
   1803  1.1  christos  * this will be called after the ddns queries have completed
   1804  1.1  christos  * if we succeeded in removing the fwd we go to the next step (if any)
   1805  1.1  christos  * if not we cleanup and leave.
   1806  1.1  christos  */
   1807  1.1  christos 
   1808  1.1  christos void
   1809  1.1  christos ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb,
   1810  1.1  christos 		  isc_result_t    eresult)
   1811  1.1  christos {
   1812  1.1  christos 	isc_result_t result = eresult;
   1813  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1814  1.1  christos 
   1815  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1816  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_rem1: %s eresult: %d",
   1817  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1818  1.1  christos #endif
   1819  1.1  christos 
   1820  1.1  christos 	switch(eresult) {
   1821  1.1  christos 	case ISC_R_SUCCESS:
   1822  1.1  christos 		/* Construct a printable form of the address for logging */
   1823  1.1  christos 		strcpy(ddns_address, piaddr(ddns_cb->address));
   1824  1.1  christos 		log_info("Removed forward map from %.*s to %s",
   1825  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1826  1.1  christos 			 (const char*)ddns_cb->fwd_name.data,
   1827  1.1  christos 			 ddns_address);
   1828  1.1  christos 
   1829  1.1  christos 		/* Do the second step of the FWD removal */
   1830  1.1  christos 		ddns_cb->state    = DDNS_STATE_REM_FW_NXRR;
   1831  1.1  christos 		ddns_cb->cur_func = ddns_fwd_srv_rem2;
   1832  1.1  christos 		result = ddns_modify_fwd(ddns_cb, MDL);
   1833  1.1  christos 		if (result == ISC_R_SUCCESS) {
   1834  1.1  christos 			return;
   1835  1.1  christos 		}
   1836  1.1  christos 		break;
   1837  1.1  christos 
   1838  1.1  christos 	case DNS_R_NXRRSET:
   1839  1.1  christos 	case DNS_R_NXDOMAIN:
   1840  1.1  christos 		/* A result of not found means rem1 did not find a guard of
   1841  1.1  christos 		 * our * type. From this we assume either there are no address
   1842  1.1  christos 		 * record(s) of our type to delete or they are unguarded and
   1843  1.1  christos 		 * therefore presumed to be static. Either way, we're done
   1844  1.1  christos 		 * unless we're in DSMM and ddns-other-guard-is-dynamic is on.
   1845  1.1  christos 		 * In which case we need to see if a guard of the other type
   1846  1.1  christos 		 * exists, which permits us to delete any address records of
   1847  1.1  christos 		 * our type. */
   1848  1.1  christos 		#define DSMM_OGD (DDNS_DUAL_STACK_MIXED_MODE | \
   1849  1.1  christos 				  DDNS_OTHER_GUARD_IS_DYNAMIC)
   1850  1.1  christos 		if ((ddns_cb->flags & DSMM_OGD) == DSMM_OGD) {
   1851  1.1  christos 			ddns_cb->state    = DDNS_STATE_REM_FW_DSMM_OTHER;
   1852  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_rem2;
   1853  1.1  christos 			result = ddns_modify_fwd(ddns_cb, MDL);
   1854  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1855  1.1  christos 				return;
   1856  1.1  christos 			}
   1857  1.1  christos 			break;
   1858  1.1  christos 		}
   1859  1.1  christos 
   1860  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1861  1.1  christos 
   1862  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1863  1.1  christos 		log_info("DDNS: no forward map to remove. %p", ddns_cb);
   1864  1.1  christos #endif
   1865  1.1  christos 		/* Trigger the add operation */
   1866  1.1  christos 		eresult = ISC_R_SUCCESS;
   1867  1.1  christos 
   1868  1.1  christos 		/* Fall through */
   1869  1.1  christos 	default:
   1870  1.1  christos 
   1871  1.1  christos 		/* We do the remove operation in most cases
   1872  1.1  christos 		 * but we don't want to continue with adding a forward
   1873  1.1  christos 		 * record if the forward removal had issues so we
   1874  1.1  christos 		 * check the eresult and set the EXECUTE_NEXT flag on
   1875  1.1  christos 		 * success.
   1876  1.1  christos 		 */
   1877  1.1  christos 
   1878  1.1  christos 		/* Do the remove operation */
   1879  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1880  1.1  christos 			/* if we have zone information get rid of it */
   1881  1.1  christos 			if (ddns_cb->zone != NULL) {
   1882  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1883  1.1  christos 			}
   1884  1.1  christos 
   1885  1.1  christos 			ddns_cb->state    = DDNS_STATE_REM_PTR;
   1886  1.1  christos 			ddns_cb->cur_func = ddns_ptr_remove;
   1887  1.1  christos 			if (eresult == ISC_R_SUCCESS)
   1888  1.1  christos 				ddns_cb->flags |= DDNS_EXECUTE_NEXT;
   1889  1.1  christos 
   1890  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1891  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1892  1.1  christos 				return;
   1893  1.1  christos 			}
   1894  1.1  christos 		}
   1895  1.1  christos 		break;
   1896  1.1  christos 	}
   1897  1.1  christos 
   1898  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1899  1.1  christos 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
   1900  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1901  1.1  christos }
   1902  1.1  christos 
   1903  1.1  christos /*%<
   1904  1.1  christos  * Remove relevant entries from DNS.
   1905  1.1  christos  *
   1906  1.1  christos  * \li lease  - lease to start with if this is for v4
   1907  1.1  christos  *
   1908  1.1  christos  * \li lease6 - lease to start with if this is for v6
   1909  1.1  christos  *
   1910  1.1  christos  * \li add_ddns_cb - control block for additional DDNS work.  This
   1911  1.1  christos  *     is used when the code is going to add a DDNS entry after removing
   1912  1.1  christos  *     the current entry.
   1913  1.1  christos  *
   1914  1.1  christos  * \li active - indication about the status of the lease. It is
   1915  1.1  christos  *     ISC_TRUE if the lease is still active, and FALSE if the lease
   1916  1.1  christos  *     is inactive.  This is used to indicate if the lease is inactive or going
   1917  1.1  christos  *     to inactive so we can avoid trying to update the lease with cb pointers
   1918  1.1  christos  *     and text information if it isn't useful.
   1919  1.1  christos  *
   1920  1.1  christos  * Returns
   1921  1.1  christos  * \li #ISC_R_FAILURE - badness occurred and we weren't able to do what was wanted
   1922  1.1  christos  * \li #ISC_R_SUCCESS - we were able to do stuff but it's in progress
   1923  1.1  christos  *
   1924  1.1  christos  * in both cases any additional block has been passed on to it's handler
   1925  1.1  christos  */
   1926  1.1  christos 
   1927  1.1  christos isc_result_t
   1928  1.1  christos ddns_removals(struct lease    *lease,
   1929  1.1  christos 	      struct iasubopt *lease6,
   1930  1.1  christos 	      dhcp_ddns_cb_t  *add_ddns_cb,
   1931  1.1  christos 	      isc_boolean_t    active)
   1932  1.1  christos {
   1933  1.1  christos 	isc_result_t rcode, execute_add = ISC_R_FAILURE;
   1934  1.1  christos 	struct binding_scope **scope = NULL;
   1935  1.1  christos 	isc_result_t result = ISC_R_FAILURE;
   1936  1.1  christos 	dhcp_ddns_cb_t        *ddns_cb = NULL;
   1937  1.1  christos 	struct data_string     leaseid;
   1938  1.1  christos 
   1939  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1940  1.1  christos 	log_info ("DDNS: ddns_removals: %s",
   1941  1.1  christos 		  dump_ddns_cb(add_ddns_cb));
   1942  1.1  christos #endif
   1943  1.1  christos 
   1944  1.1  christos 	/*
   1945  1.1  christos 	 * See if we need to cancel an outstanding request.  Mostly this is
   1946  1.1  christos 	 * used to handle the case where this routine is called twice for
   1947  1.1  christos 	 * the same release or abandon event.
   1948  1.1  christos 	 *
   1949  1.1  christos 	 * When called from the dns code as part of an update request
   1950  1.1  christos 	 * (add_ddns_cb != NULL) any outstanding requests will have already
   1951  1.1  christos 	 * been cancelled.
   1952  1.1  christos 	 *
   1953  1.1  christos 	 * If the new request is just a removal and we have an outstanding
   1954  1.1  christos 	 * request we have several options:
   1955  1.1  christos 	 *
   1956  1.1  christos 	 * - we are doing an update or we are doing a removal and the active
   1957  1.1  christos 	 * flag has changed from TRUE to FALSE.  In these cases we  need to
   1958  1.1  christos 	 * cancel the old request and start the new one.
   1959  1.1  christos 	 *
   1960  1.1  christos 	 * - other wise we are doing a removal with the active flag unchanged.
   1961  1.1  christos 	 * In this case we can let the current removal continue and do not need
   1962  1.1  christos 	 * to start a new one.  If the old request included an update to be
   1963  1.1  christos 	 * done after the removal we need to kill the update part of the
   1964  1.1  christos 	 * request.
   1965  1.1  christos 	 */
   1966  1.1  christos 
   1967  1.1  christos 	if (add_ddns_cb == NULL) {
   1968  1.1  christos 		if ((lease != NULL) && (lease->ddns_cb != NULL)) {
   1969  1.1  christos 			ddns_cb = lease->ddns_cb;
   1970  1.1  christos 
   1971  1.1  christos 			/*
   1972  1.1  christos 			 * Is the old request an update or did the
   1973  1.1  christos 			 * the active flag change?
   1974  1.1  christos 			 */
   1975  1.1  christos 			if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
   1976  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
   1977  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
   1978  1.1  christos 			    ((active == ISC_FALSE) &&
   1979  1.1  christos 			     ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
   1980  1.1  christos 				/* Cancel the current request */
   1981  1.1  christos 				ddns_cancel(lease->ddns_cb, MDL);
   1982  1.1  christos 				lease->ddns_cb = NULL;
   1983  1.1  christos 			} else {
   1984  1.1  christos 				/* Remvoval, check and remove updates */
   1985  1.1  christos 				if (ddns_cb->next_op != NULL) {
   1986  1.1  christos 					destroy_ddns_cb(ddns_cb->next_op, MDL);
   1987  1.1  christos 					ddns_cb->next_op = NULL;
   1988  1.1  christos 				}
   1989  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1990  1.1  christos 				log_info("DDNS %s(%d): removal already in "
   1991  1.1  christos 					 "progress new ddns_cb=%p",
   1992  1.1  christos 					 MDL, ddns_cb);
   1993  1.1  christos #endif
   1994  1.1  christos 				return (ISC_R_SUCCESS);
   1995  1.1  christos 			}
   1996  1.1  christos 		} else if ((lease6 != NULL) && (lease6->ddns_cb != NULL)) {
   1997  1.1  christos 			ddns_cb = lease6->ddns_cb;
   1998  1.1  christos 
   1999  1.1  christos 			/*
   2000  1.1  christos 			 * Is the old request an update or did the
   2001  1.1  christos 			 * the active flag change?
   2002  1.1  christos 			 */
   2003  1.1  christos 			if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
   2004  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
   2005  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
   2006  1.1  christos 			    ((active == ISC_FALSE) &&
   2007  1.1  christos 			     ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
   2008  1.1  christos 				/* Cancel the current request */
   2009  1.1  christos 				ddns_cancel(lease6->ddns_cb, MDL);
   2010  1.1  christos 				lease6->ddns_cb = NULL;
   2011  1.1  christos 			} else {
   2012  1.1  christos 				/* Remvoval, check and remove updates */
   2013  1.1  christos 				if (ddns_cb->next_op != NULL) {
   2014  1.1  christos 					destroy_ddns_cb(ddns_cb->next_op, MDL);
   2015  1.1  christos 					ddns_cb->next_op = NULL;
   2016  1.1  christos 				}
   2017  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2018  1.1  christos 				log_info("DDNS %s(%d): removal already in "
   2019  1.1  christos 					 "progress new ddns_cb=%p",
   2020  1.1  christos 					 MDL, ddns_cb);
   2021  1.1  christos #endif
   2022  1.1  christos 				return (ISC_R_SUCCESS);
   2023  1.1  christos 			}
   2024  1.1  christos 		}
   2025  1.1  christos 		ddns_cb = NULL;
   2026  1.1  christos 	}
   2027  1.1  christos 
   2028  1.1  christos 	/* allocate our control block */
   2029  1.1  christos 	ddns_cb = ddns_cb_alloc(MDL);
   2030  1.1  christos 	if (ddns_cb == NULL) {
   2031  1.1  christos 		goto cleanup;
   2032  1.1  christos 	}
   2033  1.1  christos 
   2034  1.1  christos 	/* Set the conflict detection flags based on global configuration */
   2035  1.1  christos 	copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
   2036  1.1  christos 
   2037  1.1  christos 	/*
   2038  1.1  christos 	 * For v4 we flag static leases so we don't try
   2039  1.1  christos 	 * and manipulate the lease later.  For v6 we don't
   2040  1.1  christos 	 * get static leases and don't need to flag them.
   2041  1.1  christos 	 */
   2042  1.1  christos 	if (lease != NULL) {
   2043  1.1  christos 		scope = &(lease->scope);
   2044  1.1  christos 		ddns_cb->address = lease->ip_addr;
   2045  1.1  christos 		if (lease->flags & STATIC_LEASE)
   2046  1.1  christos 			ddns_cb->flags |= DDNS_STATIC_LEASE;
   2047  1.1  christos 	} else if (lease6 != NULL) {
   2048  1.1  christos 		scope = &(lease6->scope);
   2049  1.1  christos 		memcpy(&ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
   2050  1.1  christos 		ddns_cb->address.len = 16;
   2051  1.1  christos 	} else
   2052  1.1  christos 		goto cleanup;
   2053  1.1  christos 
   2054  1.1  christos 	/*
   2055  1.1  christos 	 * Set the flag bit if the lease is active, that is it isn't
   2056  1.1  christos 	 * expired or released.  This is used to determine if we need
   2057  1.1  christos 	 * to update the scope information for both v4 and v6 and
   2058  1.1  christos 	 * the lease information for v6 when the response
   2059  1.1  christos 	 * from the DNS code is processed.
   2060  1.1  christos 	 */
   2061  1.1  christos 	if (active == ISC_TRUE) {
   2062  1.1  christos 		ddns_cb->flags |= DDNS_ACTIVE_LEASE;
   2063  1.1  christos 	}
   2064  1.1  christos 
   2065  1.1  christos 	/* No scope implies that DDNS has not been performed for this lease. */
   2066  1.1  christos 	if (*scope == NULL)
   2067  1.1  christos 		goto cleanup;
   2068  1.1  christos 
   2069  1.1  christos 	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
   2070  1.1  christos 	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
   2071  1.1  christos 		goto cleanup;
   2072  1.1  christos 
   2073  1.1  christos 	/* Assume that we are removing both records */
   2074  1.1  christos 	ddns_cb->flags |= DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR;
   2075  1.1  christos 
   2076  1.1  christos 	/* and that we want to do the add call */
   2077  1.1  christos 	execute_add = ISC_R_SUCCESS;
   2078  1.1  christos 
   2079  1.1  christos 	/*
   2080  1.1  christos 	 * Look up stored names.
   2081  1.1  christos 	 */
   2082  1.1  christos 
   2083  1.1  christos 	/*
   2084  1.1  christos 	 * Find the fwd name and copy it to the control block.  If we don't
   2085  1.1  christos 	 * have it we can't delete the fwd record but we can still try to
   2086  1.1  christos 	 * remove the ptr record and cleanup the lease information if the
   2087  1.1  christos 	 * client did the fwd update.
   2088  1.1  christos 	 */
   2089  1.1  christos 	if (!find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-fwd-name")) {
   2090  1.1  christos 		/* don't try and delete the A, or do the add */
   2091  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
   2092  1.1  christos 		execute_add = ISC_R_FAILURE;
   2093  1.1  christos 
   2094  1.1  christos 		/* Check if client did update */
   2095  1.1  christos 		if (find_bound_string(&ddns_cb->fwd_name, *scope,
   2096  1.1  christos 				      "ddns-client-fqdn")) {
   2097  1.1  christos 			ddns_cb->flags |= DDNS_CLIENT_DID_UPDATE;
   2098  1.1  christos 		}
   2099  1.1  christos 	}
   2100  1.1  christos 
   2101  1.1  christos 	/*
   2102  1.1  christos 	 * Find the txt or dhcid tag and copy it to the control block. If we
   2103  1.1  christos 	 * don't have one this isn't an interim or standard record so we can't
   2104  1.1  christos 	 * delete the A record using this mechanism but we can delete the ptr
   2105  1.1  christos 	 * record. In this case we will attempt to do any requested next step.
   2106  1.1  christos 	 */
   2107  1.1  christos 	memset(&leaseid, 0, sizeof(leaseid));
   2108  1.1  christos 	if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) {
   2109  1.1  christos 		/* We have a standard tag */
   2110  1.1  christos 		ddns_cb->lease_tag = ddns_standard_tag;
   2111  1.1  christos 		ddns_cb->dhcid_class = dns_rdatatype_dhcid;
   2112  1.1  christos 		ddns_cb->other_dhcid_class = dns_rdatatype_txt;
   2113  1.1  christos 		data_string_copy(&ddns_cb->dhcid, &leaseid, MDL);
   2114  1.1  christos 		data_string_forget(&leaseid, MDL);
   2115  1.1  christos 	} else 	if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) {
   2116  1.1  christos 		/* we have an interim tag */
   2117  1.1  christos 		ddns_cb->lease_tag = ddns_interim_tag;
   2118  1.1  christos 		ddns_cb->dhcid_class = dns_rdatatype_txt;
   2119  1.1  christos 		ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
   2120  1.1  christos 		if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) !=
   2121  1.1  christos 		    ISC_R_SUCCESS) {
   2122  1.1  christos 			/* We couldn't convert the dhcid from the lease
   2123  1.1  christos 			 * version to the dns version.  We can't delete
   2124  1.1  christos 			 * the A record but can continue to the ptr
   2125  1.1  christos 			 */
   2126  1.1  christos 			ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
   2127  1.1  christos 		}
   2128  1.1  christos 		data_string_forget(&leaseid, MDL);
   2129  1.1  christos 	} else {
   2130  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
   2131  1.1  christos 	}
   2132  1.1  christos 
   2133  1.1  christos 	/*
   2134  1.1  christos 	 * Find the rev name and copy it to the control block.  If we don't
   2135  1.1  christos 	 * have it we can't get rid of it but we can try to remove the fwd
   2136  1.1  christos 	 * pointer if desired.
   2137  1.1  christos 	 */
   2138  1.1  christos 	if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) {
   2139  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_PTR;
   2140  1.1  christos 	}
   2141  1.1  christos 
   2142  1.1  christos 
   2143  1.1  christos 	/*
   2144  1.1  christos 	 * If we have a second control block for doing an add
   2145  1.1  christos 	 * after the remove finished attach it to our control block.
   2146  1.1  christos 	 */
   2147  1.1  christos 	ddns_cb->next_op = add_ddns_cb;
   2148  1.1  christos 
   2149  1.1  christos 	/*
   2150  1.1  christos 	 * Now that we've collected the information we can try to process it.
   2151  1.1  christos 	 * If necessary we call an appropriate routine to send a message and
   2152  1.1  christos 	 * provide it with an action routine to run on the control block given
   2153  1.1  christos 	 * the results of the message.  We have three entry points from here,
   2154  1.1  christos 	 * one for removing the A record, the next for removing the PTR and
   2155  1.1  christos 	 * the third for doing any requested add.
   2156  1.1  christos 	 */
   2157  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
   2158  1.1  christos 		if (ddns_cb->fwd_name.len != 0) {
   2159  1.1  christos 			ddns_cb->state    = DDNS_STATE_REM_FW_YXDHCID;
   2160  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_rem1;
   2161  1.1  christos 
   2162  1.1  christos 			rcode = ddns_modify_fwd(ddns_cb, MDL);
   2163  1.1  christos 			if (rcode == ISC_R_SUCCESS) {
   2164  1.1  christos 				ddns_update_lease_ptr(lease, lease6, ddns_cb,
   2165  1.1  christos 						      ddns_cb, MDL);
   2166  1.1  christos 				return (ISC_R_SUCCESS);
   2167  1.1  christos 			}
   2168  1.1  christos 
   2169  1.1  christos 			/*
   2170  1.1  christos 			 * We weren't able to process the request tag the
   2171  1.1  christos 			 * add so we won't execute it.
   2172  1.1  christos 			 */
   2173  1.1  christos 			execute_add = ISC_R_FAILURE;
   2174  1.1  christos 			goto cleanup;
   2175  1.1  christos 		}
   2176  1.1  christos 		else {
   2177  1.1  christos 			/*remove info from scope */
   2178  1.1  christos 			unset(*scope, "ddns-fwd-name");
   2179  1.1  christos 			unset(*scope, ddns_cb->lease_tag);
   2180  1.1  christos 		}
   2181  1.1  christos 	}
   2182  1.1  christos 
   2183  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   2184  1.1  christos 		ddns_cb->state      = DDNS_STATE_REM_PTR;
   2185  1.1  christos 		ddns_cb->cur_func   = ddns_ptr_remove;
   2186  1.1  christos 		ddns_cb->flags      |= DDNS_EXECUTE_NEXT;
   2187  1.1  christos 
   2188  1.1  christos 		/*
   2189  1.1  christos 		 * if execute add isn't success remove the control block so
   2190  1.1  christos 		 * it won't be processed when the remove completes.  We
   2191  1.1  christos 		 * also arrange to clean it up and get rid of it.
   2192  1.1  christos 		 */
   2193  1.1  christos 		if (execute_add != ISC_R_SUCCESS) {
   2194  1.1  christos 		   	ddns_cb->next_op = NULL;
   2195  1.1  christos 			ddns_fwd_srv_connector(lease, lease6, scope,
   2196  1.1  christos 					       add_ddns_cb, execute_add);
   2197  1.1  christos 			add_ddns_cb = NULL;
   2198  1.1  christos 		}
   2199  1.1  christos 		else {
   2200  1.1  christos 			result = ISC_R_SUCCESS;
   2201  1.1  christos 		}
   2202  1.1  christos 
   2203  1.1  christos 		rcode = ddns_modify_ptr(ddns_cb, MDL);
   2204  1.1  christos 		if (rcode == ISC_R_SUCCESS) {
   2205  1.1  christos 			ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb,
   2206  1.1  christos 					      MDL);
   2207  1.1  christos 			return (result);
   2208  1.1  christos 		}
   2209  1.1  christos 
   2210  1.1  christos 		/* We weren't able to process the request tag the
   2211  1.1  christos 		 * add so we won't execute it */
   2212  1.1  christos 		execute_add = ISC_R_FAILURE;
   2213  1.1  christos 		goto cleanup;
   2214  1.1  christos 	}
   2215  1.1  christos 
   2216  1.1  christos  cleanup:
   2217  1.1  christos 	/*
   2218  1.1  christos 	 * We've gotten here because we didn't need to send a message or
   2219  1.1  christos 	 * we failed when trying to do so.  We send the additional cb
   2220  1.1  christos 	 * off to handle sending and/or cleanup and cleanup anything
   2221  1.1  christos 	 * we allocated here.
   2222  1.1  christos 	 */
   2223  1.1  christos 	ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add);
   2224  1.1  christos 	if (ddns_cb != NULL)
   2225  1.1  christos 		destroy_ddns_cb(ddns_cb, MDL);
   2226  1.1  christos 
   2227  1.1  christos 	return (result);
   2228  1.1  christos }
   2229  1.1  christos 
   2230  1.1  christos /* Convenience function for setting flag bits in a mask */
   2231  1.1  christos void set_flag (u_int16_t *flags,
   2232  1.1  christos 	       u_int16_t flag,
   2233  1.1  christos                u_int16_t value) {
   2234  1.1  christos 	if (flags) {
   2235  1.1  christos 		if (value) {
   2236  1.1  christos 			*flags |= flag;
   2237  1.1  christos 		} else {
   2238  1.1  christos 			*flags &= ~flag;
   2239  1.1  christos 		}
   2240  1.1  christos 	}
   2241  1.1  christos }
   2242  1.1  christos 
   2243  1.1  christos /*
   2244  1.1  christos  * Convenience function which replicates the conflict flags set in one
   2245  1.1  christos  * mask to another, while preserving all other flags.
   2246  1.1  christos  */
   2247  1.1  christos void copy_conflict_flags(u_int16_t *target,
   2248  1.1  christos 			 u_int16_t source)  {
   2249  1.1  christos 	if (target) {
   2250  1.1  christos 		/* Preserve non conflict flags */
   2251  1.1  christos 		*target &= ~CONFLICT_BITS;
   2252  1.1  christos 
   2253  1.1  christos 		/* Enable conflict flags per source */
   2254  1.1  christos 		*target |= source & CONFLICT_BITS;
   2255  1.1  christos 	}
   2256  1.1  christos }
   2257  1.1  christos 
   2258  1.1  christos /*
   2259  1.1  christos  * Given an option_state, create a mask of conflict detection flags based
   2260  1.1  christos  * on the appropriate configuration parameters within the option state.
   2261  1.1  christos  */
   2262  1.1  christos u_int16_t
   2263  1.1  christos get_conflict_mask(struct option_state *options) {
   2264  1.1  christos 
   2265  1.1  christos 	int ddns_update_conflict_detection = 1;  /* default on  */
   2266  1.1  christos         int ddns_dual_stack_mixed_mode = 0;	 /* default off */
   2267  1.1  christos         int ddns_guard_id_must_match = 1;	 /* default on  */
   2268  1.1  christos         int ddns_other_guard_is_dynamic = 0;	 /* default off */
   2269  1.1  christos 	struct option_cache *oc = NULL;
   2270  1.1  christos 
   2271  1.1  christos 	u_int16_t mask = 0;
   2272  1.1  christos 	oc = lookup_option(&server_universe, options, SV_DDNS_CONFLICT_DETECT);
   2273  1.1  christos 	if (oc) {
   2274  1.1  christos 		ddns_update_conflict_detection =
   2275  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2276  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2277  1.1  christos 	}
   2278  1.1  christos 
   2279  1.1  christos 	set_flag(&mask, DDNS_CONFLICT_DETECTION,
   2280  1.1  christos 		 ddns_update_conflict_detection);
   2281  1.1  christos 
   2282  1.1  christos 	if (!ddns_update_conflict_detection) {
   2283  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2284  1.1  christos 		log_info ("DDNS conflict detection: off");
   2285  1.1  christos #endif
   2286  1.1  christos 		/* Turn the rest of the conflict related flags off */
   2287  1.1  christos 		set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, 0);
   2288  1.1  christos 		set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, 0);
   2289  1.1  christos 		set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, 0);
   2290  1.1  christos 		return (mask);
   2291  1.1  christos 	}
   2292  1.1  christos 
   2293  1.1  christos 	// Get the values
   2294  1.1  christos         oc = lookup_option(&server_universe, options,
   2295  1.1  christos                            SV_DDNS_DUAL_STACK_MIXED_MODE);
   2296  1.1  christos         if (oc) {
   2297  1.1  christos                 ddns_dual_stack_mixed_mode =
   2298  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2299  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2300  1.1  christos         }
   2301  1.1  christos 
   2302  1.1  christos         oc = lookup_option(&server_universe, options,
   2303  1.1  christos                            SV_DDNS_GUARD_ID_MUST_MATCH);
   2304  1.1  christos         if (oc) {
   2305  1.1  christos                 ddns_guard_id_must_match =
   2306  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2307  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2308  1.1  christos         }
   2309  1.1  christos 
   2310  1.1  christos         oc = lookup_option(&server_universe, options,
   2311  1.1  christos                            SV_DDNS_OTHER_GUARD_IS_DYNAMIC);
   2312  1.1  christos         if (oc) {
   2313  1.1  christos                 ddns_other_guard_is_dynamic =
   2314  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2315  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2316  1.1  christos         }
   2317  1.1  christos 
   2318  1.1  christos 	// Set the flags
   2319  1.1  christos 	set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE,
   2320  1.1  christos 		 ddns_dual_stack_mixed_mode);
   2321  1.1  christos 
   2322  1.1  christos 	set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH,
   2323  1.1  christos 		 ddns_guard_id_must_match);
   2324  1.1  christos 
   2325  1.1  christos 	set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC,
   2326  1.1  christos 		 ddns_other_guard_is_dynamic);
   2327  1.1  christos 
   2328  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2329  1.1  christos 	log_info ("DDNS conflict behavior:\n"
   2330  1.1  christos 		  "\tddns-update-style: %s\n"
   2331  1.1  christos 		  "\tupdate-conflict-detection: %d\n"
   2332  1.1  christos 		  "\tddns-dual-stack-mixed-mode: %d\n"
   2333  1.1  christos 		  "\tddns-guard-id-must-match %d\n"
   2334  1.1  christos 		  "\tddns-other-guard-is-dynamic: %d\n",
   2335  1.1  christos 		  ddns_styles_values[ddns_update_style].name,
   2336  1.1  christos 		  ddns_update_conflict_detection,
   2337  1.1  christos 		  ddns_dual_stack_mixed_mode,
   2338  1.1  christos 		  ddns_guard_id_must_match,
   2339  1.1  christos 		  ddns_other_guard_is_dynamic);
   2340  1.1  christos #endif
   2341  1.1  christos 	return (mask);
   2342  1.1  christos }
   2343  1.1  christos 
   2344  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2345  1.1  christos /* Type used for creating lists of function pointers and their names */
   2346  1.1  christos typedef struct {
   2347  1.1  christos 	void *ptr;
   2348  1.1  christos 	char *name;
   2349  1.1  christos } LabeledPtr;
   2350  1.1  christos 
   2351  1.1  christos /* Returns the name of the function referred to by the given address */
   2352  1.1  christos char*
   2353  1.1  christos dump_ddns_cb_func(void *func) {
   2354  1.1  christos 	static LabeledPtr funcs[] = {
   2355  1.1  christos 		{ ddns_ptr_add, "ddns_ptr_add" },
   2356  1.1  christos 		{ ddns_fwd_srv_add2, "ddns_fwd_srv_add2" },
   2357  1.1  christos 		{ ddns_fwd_srv_add1, "ddns_fwd_srv_add1" },
   2358  1.1  christos 		{ ddns_ptr_remove, "ddns_ptr_remove" },
   2359  1.1  christos 		{ ddns_fwd_srv_rem2, "ddns_fwd_srv_rem2" },
   2360  1.1  christos 		{ ddns_fwd_srv_rem1, "ddns_fwd_srv_rem1" },
   2361  1.1  christos 		{ ddns_fwd_srv_add3, "ddns_fwd_srv_adde" },
   2362  1.1  christos 		{ NULL, "unknown" }
   2363  1.1  christos 	};
   2364  1.1  christos 
   2365  1.1  christos 	LabeledPtr* lp = funcs;
   2366  1.1  christos 	if (!func) {
   2367  1.1  christos 		return ("<null>");
   2368  1.1  christos 	}
   2369  1.1  christos 
   2370  1.1  christos 	while ((lp->ptr) && (lp->ptr != func)) {
   2371  1.1  christos 		++lp;
   2372  1.1  christos 	}
   2373  1.1  christos 
   2374  1.1  christos 	return (lp->name);
   2375  1.1  christos }
   2376  1.1  christos 
   2377  1.1  christos /* Dumps basic control block info to the log */
   2378  1.1  christos char*
   2379  1.1  christos dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb) {
   2380  1.1  christos 	static char output_buf[4096];
   2381  1.1  christos 	if (!ddns_cb) {
   2382  1.1  christos 		return ("<ddns_cb is null>");
   2383  1.1  christos 	}
   2384  1.1  christos 
   2385  1.1  christos 	sprintf (output_buf, "ddns_cb: %p flags: %x state: %s cur_func: %s",
   2386  1.1  christos 		ddns_cb, ddns_cb->flags,
   2387  1.1  christos 		ddns_state_name(ddns_cb->state),
   2388  1.1  christos 		dump_ddns_cb_func(ddns_cb->cur_func));
   2389  1.1  christos 
   2390  1.1  christos 	return(output_buf);
   2391  1.1  christos }
   2392  1.1  christos #endif /* DEBUG_DNS_UPDATES */
   2393  1.1  christos 
   2394  1.1  christos #endif /* NSUPDATE */
   2395