Home | History | Annotate | Line # | Download | only in server
      1  1.2  christos /*	$NetBSD: ddns.c,v 1.4 2022/04/03 01:10:59 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.4  christos  * Copyright (C) 2004-2022 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.4  christos  *   PO Box 360
     26  1.4  christos  *   Newmarket, NH 03857 USA
     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.2  christos __RCSID("$NetBSD: ddns.c,v 1.4 2022/04/03 01:10:59 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.2  christos static 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.2  christos #ifdef notdef
    943  1.1  christos /*
    944  1.1  christos  * This function should be called when update_lease_ptr function fails.
    945  1.1  christos  * It does inform user about the condition, provides some hints how to
    946  1.1  christos  * resolve this and dies gracefully. This can happend in at least three
    947  1.1  christos  * cases (all are configuration mistakes):
    948  1.1  christos  * a) IPv4: user have duplicate fixed-address entries (the same
    949  1.1  christos  *    address is defined twice). We may have found wrong lease.
    950  1.1  christos  * b) IPv6: user have overlapping pools (we tried to find
    951  1.1  christos  *    a lease in a wrong pool)
    952  1.1  christos  * c) IPv6: user have duplicate fixed-address6 entires (the same
    953  1.1  christos  *    address is defined twice). We may have found wrong lease.
    954  1.1  christos  *
    955  1.1  christos  * Comment: while it would be possible to recover from both cases
    956  1.1  christos  * by forcibly searching for leases in *all* following pools, that would
    957  1.1  christos  * only hide the real problem - a misconfiguration. Proper solution
    958  1.1  christos  * is to log the problem, die and let the user fix his config file.
    959  1.1  christos  */
    960  1.1  christos void
    961  1.1  christos update_lease_failed(struct lease *lease,
    962  1.1  christos 		    struct iasubopt *lease6,
    963  1.1  christos 		    dhcp_ddns_cb_t  *ddns_cb,
    964  1.1  christos 		    dhcp_ddns_cb_t  *ddns_cb_set,
    965  1.1  christos 		    const char * file, int line)
    966  1.1  christos {
    967  1.1  christos 	char lease_address[MAX_ADDRESS_STRING_LEN + 64];
    968  1.1  christos 	char reason[128]; /* likely reason */
    969  1.1  christos 
    970  1.1  christos 	sprintf(reason, "unknown");
    971  1.1  christos 	sprintf(lease_address, "unknown");
    972  1.1  christos 
    973  1.1  christos 	/*
    974  1.1  christos 	 * let's pretend that everything is ok, so we can continue for
    975  1.1  christos 	 * information gathering purposes
    976  1.1  christos 	 */
    977  1.1  christos 
    978  1.1  christos 	if (ddns_cb != NULL) {
    979  1.1  christos 		strncpy(lease_address, piaddr(ddns_cb->address),
    980  1.1  christos 			MAX_ADDRESS_STRING_LEN);
    981  1.1  christos 
    982  1.1  christos 		if (ddns_cb->address.len == 4) {
    983  1.1  christos 			sprintf(reason, "duplicate IPv4 fixed-address entry");
    984  1.1  christos 		} else if (ddns_cb->address.len == 16) {
    985  1.1  christos 			sprintf(reason, "duplicate IPv6 fixed-address6 entry "
    986  1.1  christos 				"or overlapping pools");
    987  1.1  christos 		} else {
    988  1.1  christos 			/*
    989  1.1  christos 			 * Should not happen. We have non-IPv4, non-IPv6
    990  1.1  christos 			 * address. Something is very wrong here.
    991  1.1  christos 			 */
    992  1.1  christos 			sprintf(reason, "corrupted ddns_cb structure (address "
    993  1.1  christos 				"length is %d)", ddns_cb->address.len);
    994  1.1  christos 		}
    995  1.1  christos 	}
    996  1.1  christos 
    997  1.1  christos 	log_error("Failed to properly update internal lease structure with "
    998  1.1  christos 		  "DDNS");
    999  1.1  christos 	log_error("control block structures. Tried to update lease for"
   1000  1.1  christos 		  "%s address, ddns_cb=%p.", lease_address, ddns_cb);
   1001  1.1  christos 
   1002  1.1  christos 	log_error("%s", "");
   1003  1.1  christos 	log_error("This condition can occur, if DHCP server configuration is "
   1004  1.1  christos 		  "inconsistent.");
   1005  1.1  christos 	log_error("In particular, please do check that your configuration:");
   1006  1.1  christos 	log_error("a) does not have overlapping pools (especially containing");
   1007  1.1  christos 	log_error("   %s address).", lease_address);
   1008  1.1  christos 	log_error("b) there are no duplicate fixed-address or fixed-address6");
   1009  1.1  christos 	log_error("entries for the %s address.", lease_address);
   1010  1.1  christos 	log_error("%s", "");
   1011  1.1  christos 	log_error("Possible reason for this failure: %s", reason);
   1012  1.1  christos 
   1013  1.1  christos 	log_fatal("%s(%d): Failed to update lease database with DDNS info for "
   1014  1.1  christos 		  "address %s. Lease database inconsistent. Unable to recover."
   1015  1.1  christos 		  " Terminating.", file, line, lease_address);
   1016  1.1  christos }
   1017  1.2  christos #endif
   1018  1.1  christos 
   1019  1.1  christos /*
   1020  1.1  christos  * utility function to update found lease. It does extra checks
   1021  1.1  christos  * that we are indeed updating the right lease. It may happen
   1022  1.1  christos  * that user have duplicate fixed-address entries, so we attempt
   1023  1.1  christos  * to update wrong lease. See also safe_lease6_update.
   1024  1.1  christos  */
   1025  1.1  christos 
   1026  1.2  christos static void
   1027  1.1  christos safe_lease_update(struct lease *lease,
   1028  1.1  christos 		  dhcp_ddns_cb_t *oldcb,
   1029  1.1  christos 		  dhcp_ddns_cb_t *newcb,
   1030  1.1  christos 		  const char *file, int line)
   1031  1.1  christos {
   1032  1.1  christos 	if (lease == NULL) {
   1033  1.1  christos 		/* should never get here */
   1034  1.1  christos 		log_fatal("Impossible condition at %s:%d (called from %s:%d).",
   1035  1.1  christos 			  MDL, file, line);
   1036  1.1  christos 	}
   1037  1.1  christos 
   1038  1.1  christos 	if ( (lease->ddns_cb == NULL) && (newcb == NULL) ) {
   1039  1.1  christos 		/*
   1040  1.1  christos 		 * Trying to clean up pointer that is already null. We
   1041  1.1  christos 		 * are most likely trying to update wrong lease here.
   1042  1.1  christos 		 */
   1043  1.1  christos 
   1044  1.1  christos 		/*
   1045  1.1  christos 		 * Previously this error message popped out during
   1046  1.1  christos 		 * DNS update for fixed leases.  As we no longer
   1047  1.1  christos 		 * try to update the lease for a fixed (static) lease
   1048  1.1  christos 		 * this should not be a problem.
   1049  1.1  christos 		 */
   1050  1.1  christos 		log_error("%s(%d): Invalid lease update. Tried to "
   1051  1.1  christos 			  "clear already NULL DDNS control block "
   1052  1.1  christos 			  "pointer for lease %s.",
   1053  1.1  christos 			  file, line, piaddr(lease->ip_addr) );
   1054  1.1  christos 
   1055  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1056  1.1  christos 		update_lease_failed(lease, NULL, oldcb, newcb, file, line);
   1057  1.1  christos #endif
   1058  1.1  christos 		/*
   1059  1.1  christos 		 * May not reach this: update_lease_failed calls
   1060  1.1  christos 		 * log_fatal.
   1061  1.1  christos 		 */
   1062  1.1  christos 		return;
   1063  1.1  christos 	}
   1064  1.1  christos 
   1065  1.1  christos 	if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) {
   1066  1.1  christos 		/*
   1067  1.1  christos 		 * There is existing cb structure, but it differs from
   1068  1.1  christos 		 * what we expected to see there. Most likely we are
   1069  1.1  christos 		 * trying to update wrong lease.
   1070  1.1  christos 		 */
   1071  1.1  christos 		log_error("%s(%d): Failed to update internal lease "
   1072  1.1  christos 			  "structure with DDNS control block. Existing"
   1073  1.1  christos 			  " ddns_cb structure does not match "
   1074  1.1  christos 			  "expectations.IPv4=%s, old ddns_cb=%p, tried"
   1075  1.1  christos 			  "to update to new ddns_cb=%p", file, line,
   1076  1.1  christos 			  piaddr(lease->ip_addr), oldcb,  newcb);
   1077  1.1  christos 
   1078  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1079  1.1  christos 		update_lease_failed(lease, NULL, oldcb, newcb, file, line);
   1080  1.1  christos #endif
   1081  1.1  christos 		/*
   1082  1.1  christos 		 * May not reach this: update_lease_failed calls
   1083  1.1  christos 		 * log_fatal.
   1084  1.1  christos 		 */
   1085  1.1  christos 		return;
   1086  1.1  christos 	}
   1087  1.1  christos 
   1088  1.1  christos 	/* additional IPv4 specific checks may be added here */
   1089  1.1  christos 
   1090  1.1  christos 	/* update the lease */
   1091  1.1  christos 	lease->ddns_cb = newcb;
   1092  1.1  christos }
   1093  1.1  christos 
   1094  1.2  christos static void
   1095  1.1  christos safe_lease6_update(struct iasubopt *lease6,
   1096  1.1  christos 		   dhcp_ddns_cb_t *oldcb,
   1097  1.1  christos 		   dhcp_ddns_cb_t *newcb,
   1098  1.1  christos 		   const char *file, int line)
   1099  1.1  christos {
   1100  1.1  christos 	char addrbuf[MAX_ADDRESS_STRING_LEN];
   1101  1.1  christos 
   1102  1.1  christos 	if (lease6 == NULL) {
   1103  1.1  christos 		/* should never get here */
   1104  1.1  christos 		log_fatal("Impossible condition at %s:%d (called from %s:%d).",
   1105  1.1  christos 			  MDL, file, line);
   1106  1.1  christos 	}
   1107  1.1  christos 
   1108  1.1  christos 	if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) {
   1109  1.1  christos 		inet_ntop(AF_INET6, &lease6->addr, addrbuf,
   1110  1.1  christos 			  MAX_ADDRESS_STRING_LEN);
   1111  1.1  christos 		/*
   1112  1.1  christos 		 * Trying to clean up pointer that is already null. We
   1113  1.1  christos 		 * are most likely trying to update wrong lease here.
   1114  1.1  christos 		 */
   1115  1.1  christos 		log_error("%s(%d): Failed to update internal lease "
   1116  1.1  christos 			  "structure. Tried to clear already NULL "
   1117  1.1  christos 			  "DDNS control block pointer for lease %s.",
   1118  1.1  christos 			  file, line, addrbuf);
   1119  1.1  christos 
   1120  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1121  1.1  christos 		update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
   1122  1.1  christos #endif
   1123  1.1  christos 
   1124  1.1  christos 		/*
   1125  1.1  christos 		 * May not reach this: update_lease_failed calls
   1126  1.1  christos 		 * log_fatal.
   1127  1.1  christos 		 */
   1128  1.1  christos 		return;
   1129  1.1  christos 	}
   1130  1.1  christos 
   1131  1.1  christos 	if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) {
   1132  1.1  christos 		/*
   1133  1.1  christos 		 * there is existing cb structure, but it differs from
   1134  1.1  christos 		 * what we expected to see there. Most likely we are
   1135  1.1  christos 		 * trying to update wrong lease.
   1136  1.1  christos 		 */
   1137  1.1  christos 		inet_ntop(AF_INET6, &lease6->addr, addrbuf,
   1138  1.1  christos 			  MAX_ADDRESS_STRING_LEN);
   1139  1.1  christos 
   1140  1.1  christos 		log_error("%s(%d): Failed to update internal lease "
   1141  1.1  christos 			  "structure with DDNS control block. Existing"
   1142  1.1  christos 			  " ddns_cb structure does not match "
   1143  1.1  christos 			  "expectations.IPv6=%s, old ddns_cb=%p, tried"
   1144  1.1  christos 			  "to update to new ddns_cb=%p", file, line,
   1145  1.1  christos 			  addrbuf, oldcb,  newcb);
   1146  1.1  christos 
   1147  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1148  1.1  christos 		update_lease_failed(NULL, lease6, oldcb, newcb, file, line);
   1149  1.1  christos #endif
   1150  1.1  christos 		/*
   1151  1.1  christos 		 * May not reach this: update_lease_failed calls
   1152  1.1  christos 		 * log_fatal.
   1153  1.1  christos 		 */
   1154  1.1  christos 		return;
   1155  1.1  christos 	}
   1156  1.1  christos 	/* additional IPv6 specific checks may be added here */
   1157  1.1  christos 
   1158  1.1  christos 	/* update the lease */
   1159  1.1  christos 	lease6->ddns_cb = newcb;
   1160  1.1  christos }
   1161  1.1  christos 
   1162  1.1  christos /*
   1163  1.1  christos  * Utility function to update the pointer to the DDNS control block
   1164  1.1  christos  * in a lease.
   1165  1.1  christos  * SUCCESS - able to update the pointer
   1166  1.1  christos  * FAILURE - lease didn't exist or sanity checks failed
   1167  1.1  christos  * lease and lease6 may be empty in which case we attempt to find
   1168  1.1  christos  * the lease from the ddns_cb information.
   1169  1.1  christos  * ddns_cb is the control block to use if a lookup is necessary
   1170  1.1  christos  * ddns_cb_set is the pointer to insert into the lease and may be NULL
   1171  1.1  christos  * The last two arguments may look odd as they will be the same much of the
   1172  1.1  christos  * time, but I need an argument to tell me if I'm setting or clearing in
   1173  1.1  christos  * addition to the address information from the cb to look up the lease.
   1174  1.1  christos  * using the same value twice allows me more flexibility.
   1175  1.1  christos  */
   1176  1.1  christos 
   1177  1.2  christos static isc_result_t
   1178  1.1  christos ddns_update_lease_ptr(struct lease    *lease,
   1179  1.1  christos 		      struct iasubopt *lease6,
   1180  1.1  christos 		      dhcp_ddns_cb_t  *ddns_cb,
   1181  1.1  christos 		      dhcp_ddns_cb_t  *ddns_cb_set,
   1182  1.1  christos 		      const char * file, int line)
   1183  1.1  christos {
   1184  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1185  1.1  christos 	sprintf(ddns_address, "unknown");
   1186  1.1  christos 	if (ddns_cb == NULL) {
   1187  1.1  christos 		log_info("%s(%d): No control block for lease update",
   1188  1.1  christos 			 file, line);
   1189  1.1  christos 		return (ISC_R_FAILURE);
   1190  1.1  christos 	}
   1191  1.1  christos 	else {
   1192  1.1  christos 		strcpy(ddns_address, piaddr(ddns_cb->address));
   1193  1.1  christos 	}
   1194  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1195  1.1  christos 	log_info("%s(%d): Updating lease_ptr for ddns_cp=%p (addr=%s)",
   1196  1.1  christos 		 file, line, ddns_cb, ddns_address );
   1197  1.1  christos #endif
   1198  1.1  christos 
   1199  1.1  christos 	/*
   1200  1.1  christos 	 * If the lease was static (for a fixed address)
   1201  1.1  christos 	 * we don't need to do any work.
   1202  1.1  christos 	 */
   1203  1.1  christos 	if (ddns_cb->flags & DDNS_STATIC_LEASE) {
   1204  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1205  1.1  christos 		log_info("lease is static, returning");
   1206  1.1  christos #endif
   1207  1.1  christos 		return (ISC_R_SUCCESS);
   1208  1.1  christos 	}
   1209  1.1  christos 
   1210  1.1  christos 	/*
   1211  1.1  christos 	 * If we are processing an expired or released v6 lease
   1212  1.1  christos 	 * we don't actually have a lease to update
   1213  1.1  christos 	 */
   1214  1.1  christos 	if ((ddns_cb->address.len == 16) &&
   1215  1.1  christos 	    ((ddns_cb->flags & DDNS_ACTIVE_LEASE) == 0)) {
   1216  1.1  christos 		return (ISC_R_SUCCESS);
   1217  1.1  christos 	}
   1218  1.1  christos 
   1219  1.1  christos 	if (lease != NULL) {
   1220  1.1  christos 		safe_lease_update(lease, ddns_cb, ddns_cb_set,
   1221  1.1  christos 				  file, line);
   1222  1.1  christos 	} else if (lease6 != NULL) {
   1223  1.1  christos 		safe_lease6_update(lease6, ddns_cb, ddns_cb_set,
   1224  1.1  christos 				  file, line);
   1225  1.1  christos 	} else if (ddns_cb->address.len == 4) {
   1226  1.1  christos 		struct lease *find_lease = NULL;
   1227  1.1  christos 		if (find_lease_by_ip_addr(&find_lease,
   1228  1.1  christos 					  ddns_cb->address, MDL) != 0) {
   1229  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1230  1.1  christos 			log_info("%s(%d): find_lease_by_ip_addr(%s) successful:"
   1231  1.1  christos 				 "lease=%p", file, line, ddns_address,
   1232  1.1  christos 				 find_lease);
   1233  1.1  christos #endif
   1234  1.1  christos 
   1235  1.1  christos 			safe_lease_update(find_lease, ddns_cb,
   1236  1.1  christos 					  ddns_cb_set, file, line);
   1237  1.1  christos 			lease_dereference(&find_lease, MDL);
   1238  1.1  christos 		}
   1239  1.1  christos 		else {
   1240  1.1  christos 			log_error("%s(%d): ddns_update_lease_ptr failed. "
   1241  1.1  christos 				  "Lease for %s not found.",
   1242  1.1  christos 				  file, line, piaddr(ddns_cb->address));
   1243  1.1  christos 
   1244  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1245  1.1  christos 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
   1246  1.1  christos 					    file, line);
   1247  1.1  christos #endif
   1248  1.1  christos 			/*
   1249  1.1  christos 			 * may not reach this. update_lease_failed
   1250  1.1  christos 			 * calls log_fatal.
   1251  1.1  christos 			 */
   1252  1.1  christos 			return(ISC_R_FAILURE);
   1253  1.1  christos 
   1254  1.1  christos 		}
   1255  1.1  christos 	} else if (ddns_cb->address.len == 16) {
   1256  1.1  christos 		struct iasubopt *find_lease6 = NULL;
   1257  1.1  christos 		struct ipv6_pool *pool = NULL;
   1258  1.1  christos 		struct in6_addr addr;
   1259  1.1  christos 		char addrbuf[MAX_ADDRESS_STRING_LEN];
   1260  1.1  christos 
   1261  1.1  christos 		memcpy(&addr, &ddns_cb->address.iabuf, 16);
   1262  1.1  christos 		if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) !=
   1263  1.1  christos 		     ISC_R_SUCCESS) &&
   1264  1.1  christos 		    (find_ipv6_pool(&pool, D6O_IA_NA, &addr) !=
   1265  1.1  christos 		     ISC_R_SUCCESS)) {
   1266  1.1  christos 			inet_ntop(AF_INET6, &addr, addrbuf,
   1267  1.1  christos 				  MAX_ADDRESS_STRING_LEN);
   1268  1.1  christos 			log_error("%s(%d): Pool for lease %s not found.",
   1269  1.1  christos 				  file, line, addrbuf);
   1270  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1271  1.1  christos 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
   1272  1.1  christos 					    file, line);
   1273  1.1  christos #endif
   1274  1.1  christos 			/*
   1275  1.1  christos 			 * never reached. update_lease_failed
   1276  1.1  christos 			 * calls log_fatal.
   1277  1.1  christos 			 */
   1278  1.1  christos 			return(ISC_R_FAILURE);
   1279  1.1  christos 		}
   1280  1.1  christos 
   1281  1.1  christos 		if (iasubopt_hash_lookup(&find_lease6, pool->leases,
   1282  1.1  christos 					 &addr, 16, MDL)) {
   1283  1.1  christos 			find_lease6->ddns_cb = ddns_cb_set;
   1284  1.1  christos 			iasubopt_dereference(&find_lease6, MDL);
   1285  1.1  christos 		} else {
   1286  1.1  christos 			inet_ntop(AF_INET6, &addr, addrbuf,
   1287  1.1  christos 				  MAX_ADDRESS_STRING_LEN);
   1288  1.1  christos 			log_error("%s(%d): Lease %s not found within pool.",
   1289  1.1  christos 				  file, line, addrbuf);
   1290  1.1  christos #if defined (DNS_UPDATES_MEMORY_CHECKS)
   1291  1.1  christos 			update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
   1292  1.1  christos 					    file, line);
   1293  1.1  christos #endif
   1294  1.1  christos 			/*
   1295  1.3  christos 			 * not reached when update_lease_failed is called,
   1296  1.3  christos 			 * it calls log_fatal.
   1297  1.1  christos 			 */
   1298  1.3  christos 			ipv6_pool_dereference(&pool, MDL);
   1299  1.1  christos 			return(ISC_R_FAILURE);
   1300  1.1  christos 		}
   1301  1.1  christos 		ipv6_pool_dereference(&pool, MDL);
   1302  1.1  christos 	} else {
   1303  1.1  christos 		/* shouldn't get here */
   1304  1.1  christos 		log_fatal("Impossible condition at %s:%d, called from %s:%d.",
   1305  1.1  christos 			  MDL, file, line);
   1306  1.1  christos 	}
   1307  1.1  christos 
   1308  1.1  christos 	return(ISC_R_SUCCESS);
   1309  1.1  christos }
   1310  1.1  christos 
   1311  1.2  christos static void
   1312  1.1  christos ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb,
   1313  1.1  christos 	     isc_result_t    eresult)
   1314  1.1  christos {
   1315  1.1  christos 	if (eresult == ISC_R_SUCCESS) {
   1316  1.1  christos 		log_info("Added reverse map from %.*s to %.*s",
   1317  1.1  christos 			 (int)ddns_cb->rev_name.len,
   1318  1.1  christos 			 (const char *)ddns_cb->rev_name.data,
   1319  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1320  1.1  christos 			 (const char *)ddns_cb->fwd_name.data);
   1321  1.1  christos 
   1322  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1323  1.1  christos 	} else {
   1324  1.1  christos 		log_error("Unable to add reverse map from %.*s to %.*s: %s",
   1325  1.1  christos 			  (int)ddns_cb->rev_name.len,
   1326  1.1  christos 			  (const char *)ddns_cb->rev_name.data,
   1327  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1328  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1329  1.1  christos 			  isc_result_totext (eresult));
   1330  1.1  christos 	}
   1331  1.1  christos 
   1332  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1333  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1334  1.1  christos 	/*
   1335  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1336  1.1  christos 	 * the current state as the prerequisites for the first message
   1337  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1338  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1339  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1340  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1341  1.1  christos 	 * here rather than immediately after the call to update the lease
   1342  1.1  christos 	 * text in order to save any previously written data.
   1343  1.1  christos 	 */
   1344  1.1  christos 	commit_leases();
   1345  1.1  christos 	return;
   1346  1.1  christos }
   1347  1.1  christos 
   1348  1.1  christos /*
   1349  1.1  christos  * action routine when trying to remove a pointer
   1350  1.1  christos  * this will be called after the ddns queries have completed
   1351  1.1  christos  * if we succeeded in removing the pointer we go to the next step (if any)
   1352  1.1  christos  * if not we cleanup and leave.
   1353  1.1  christos  */
   1354  1.1  christos 
   1355  1.2  christos static void
   1356  1.1  christos ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb,
   1357  1.1  christos 		isc_result_t    eresult)
   1358  1.1  christos {
   1359  1.1  christos 	isc_result_t result = eresult;
   1360  1.1  christos 
   1361  1.1  christos 	switch(eresult) {
   1362  1.1  christos 	case ISC_R_SUCCESS:
   1363  1.1  christos 		log_info("Removed reverse map on %.*s",
   1364  1.1  christos 			 (int)ddns_cb->rev_name.len,
   1365  1.1  christos 			 (const char *)ddns_cb->rev_name.data);
   1366  1.1  christos 		/* fall through */
   1367  1.1  christos 	case DNS_R_NXRRSET:
   1368  1.1  christos 	case DNS_R_NXDOMAIN:
   1369  1.1  christos 		/* No entry is the same as success.
   1370  1.1  christos 		 * Remove the information from the lease and
   1371  1.1  christos 		 * continue with any next step */
   1372  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1373  1.1  christos 
   1374  1.1  christos 		/* trigger any add operation */
   1375  1.1  christos 		result = ISC_R_SUCCESS;
   1376  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1377  1.1  christos 		log_info("DDNS: removed map or no reverse map to remove %.*s",
   1378  1.1  christos 			 (int)ddns_cb->rev_name.len,
   1379  1.1  christos 			 (const char *)ddns_cb->rev_name.data);
   1380  1.1  christos #endif
   1381  1.1  christos 		break;
   1382  1.1  christos 
   1383  1.1  christos 	default:
   1384  1.1  christos 		log_error("Can't remove reverse map on %.*s: %s",
   1385  1.1  christos 			  (int)ddns_cb->rev_name.len,
   1386  1.1  christos 			  (const char *)ddns_cb->rev_name.data,
   1387  1.1  christos 			  isc_result_totext (eresult));
   1388  1.1  christos 		break;
   1389  1.1  christos 	}
   1390  1.1  christos 
   1391  1.1  christos 	/* If we aren't suppossed to do the next step, set the result
   1392  1.1  christos 	 * flag so ddns_fwd_srv_connector won't do much
   1393  1.1  christos 	 */
   1394  1.1  christos 	if ((ddns_cb->flags & DDNS_EXECUTE_NEXT) == 0)
   1395  1.1  christos 		result = ISC_R_FAILURE;
   1396  1.1  christos 
   1397  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1398  1.1  christos 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result);
   1399  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1400  1.1  christos 	return;
   1401  1.1  christos }
   1402  1.1  christos 
   1403  1.1  christos 
   1404  1.1  christos /*
   1405  1.1  christos  * If the first query succeeds, the updater can conclude that it
   1406  1.1  christos  * has added a new name whose only RRs are the A and DHCID RR records.
   1407  1.1  christos  * The A RR update is now complete (and a client updater is finished,
   1408  1.1  christos  * while a server might proceed to perform a PTR RR update).
   1409  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1410  1.1  christos  *
   1411  1.1  christos  * If the second query succeeds, the updater can conclude that the current
   1412  1.1  christos  * client was the last client associated with the domain name, and that
   1413  1.1  christos  * the name now contains the updated A RR. The A RR update is now
   1414  1.1  christos  * complete (and a client updater is finished, while a server would
   1415  1.1  christos  * then proceed to perform a PTR RR update).
   1416  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1417  1.1  christos  *
   1418  1.1  christos  * If the second query fails with NXRRSET, the updater must conclude
   1419  1.1  christos  * that the client's desired name is in use by another host.  If
   1420  1.1  christos  * Dual Stack Mixed Mode (DSMM) is enabled and we proceed to a
   1421  1.1  christos  * third stage forward update attempt specific to DSMM rules.  If not,
   1422  1.1  christos  * then the existing entries are left intact:
   1423  1.1  christos  *
   1424  1.1  christos  * At this juncture, the updater can decide (based on some administrative
   1425  1.1  christos  * configuration outside of the scope of this document) whether to let
   1426  1.1  christos  * the existing owner of the name keep that name, and to (possibly)
   1427  1.1  christos  * perform some name disambiguation operation on behalf of the current
   1428  1.1  christos  * client, or to replace the RRs on the name with RRs that represent
   1429  1.1  christos  * the current client. If the configured policy allows replacement of
   1430  1.1  christos  * existing records, the updater submits a query that deletes the
   1431  1.1  christos  * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
   1432  1.1  christos  * represent the IP address and client-identity of the new client.
   1433  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1434  1.1  christos  */
   1435  1.1  christos 
   1436  1.2  christos static void
   1437  1.1  christos ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb,
   1438  1.1  christos 		  isc_result_t    eresult)
   1439  1.1  christos {
   1440  1.1  christos 	isc_result_t result;
   1441  1.1  christos 	const char *logstr = NULL;
   1442  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1443  1.1  christos 
   1444  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1445  1.1  christos 	log_info ("DDNS:ddns_fwd_srv_add2: %s eresult: %d",
   1446  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1447  1.1  christos #endif
   1448  1.1  christos 
   1449  1.1  christos 	/* Construct a printable form of the address for logging */
   1450  1.1  christos 	strcpy(ddns_address, piaddr(ddns_cb->address));
   1451  1.1  christos 
   1452  1.1  christos 	switch(eresult) {
   1453  1.1  christos 	case ISC_R_SUCCESS:
   1454  1.1  christos 		log_info("Added new forward map from %.*s to %s",
   1455  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1456  1.1  christos 			 (const char *)ddns_cb->fwd_name.data,
   1457  1.1  christos 			 ddns_address);
   1458  1.1  christos 
   1459  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1460  1.1  christos 
   1461  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1462  1.1  christos 			/* if we have zone information get rid of it */
   1463  1.1  christos 			if (ddns_cb->zone != NULL) {
   1464  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1465  1.1  christos 			}
   1466  1.1  christos 
   1467  1.1  christos 			ddns_cb->state = DDNS_STATE_ADD_PTR;
   1468  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1469  1.1  christos 
   1470  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1471  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1472  1.1  christos 				return;
   1473  1.1  christos 			}
   1474  1.1  christos 		}
   1475  1.1  christos 		break;
   1476  1.1  christos 
   1477  1.1  christos 	case DNS_R_YXRRSET:
   1478  1.1  christos 	case DNS_R_YXDOMAIN:
   1479  1.1  christos 		logstr = "DHCID mismatch, belongs to another client.";
   1480  1.1  christos 		break;
   1481  1.1  christos 
   1482  1.1  christos 	case DNS_R_NXDOMAIN:
   1483  1.1  christos 	case DNS_R_NXRRSET:
   1484  1.1  christos 		/* If DSMM is on we need to try forward add3 */
   1485  1.1  christos 		if (ddns_cb->flags & DDNS_DUAL_STACK_MIXED_MODE) {
   1486  1.1  christos 			ddns_cb->state = DDNS_STATE_DSMM_FW_ADD3;
   1487  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_add3;
   1488  1.1  christos 
   1489  1.1  christos 			result = ddns_modify_fwd(ddns_cb, MDL);
   1490  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1491  1.1  christos 				return;
   1492  1.1  christos 			}
   1493  1.1  christos 
   1494  1.1  christos 			break;
   1495  1.1  christos 		}
   1496  1.1  christos 
   1497  1.1  christos 		logstr = "Has an address record but no DHCID, not mine.";
   1498  1.1  christos 		break;
   1499  1.1  christos 
   1500  1.1  christos 	default:
   1501  1.1  christos 		logstr = isc_result_totext(eresult);
   1502  1.1  christos 		break;
   1503  1.1  christos 	}
   1504  1.1  christos 
   1505  1.1  christos 	if (logstr != NULL) {
   1506  1.1  christos 		log_error("Forward map from %.*s to %s FAILED: %s",
   1507  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1508  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1509  1.1  christos 			  ddns_address, logstr);
   1510  1.1  christos 	}
   1511  1.1  christos 
   1512  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1513  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1514  1.1  christos 	/*
   1515  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1516  1.1  christos 	 * the current state as the prerequisites for the first message
   1517  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1518  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1519  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1520  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1521  1.1  christos 	 * here rather than immediately after the call to update the lease
   1522  1.1  christos 	 * text in order to save any previously written data.
   1523  1.1  christos 	 */
   1524  1.1  christos 	commit_leases();
   1525  1.1  christos 	return;
   1526  1.1  christos }
   1527  1.1  christos 
   1528  1.2  christos static void
   1529  1.1  christos ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb,
   1530  1.1  christos 		  isc_result_t    eresult)
   1531  1.1  christos {
   1532  1.1  christos 	isc_result_t result;
   1533  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1534  1.1  christos 
   1535  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1536  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_add1: %s eresult: %d",
   1537  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1538  1.1  christos #endif
   1539  1.1  christos 
   1540  1.1  christos 	/* Construct a printable form of the address for logging */
   1541  1.1  christos 	strcpy(ddns_address, piaddr(ddns_cb->address));
   1542  1.1  christos 
   1543  1.1  christos 	switch(eresult) {
   1544  1.1  christos 	case ISC_R_SUCCESS:
   1545  1.1  christos 		log_info ("Added new forward map from %.*s to %s",
   1546  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1547  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1548  1.1  christos 			  ddns_address);
   1549  1.1  christos 
   1550  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1551  1.1  christos 
   1552  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1553  1.1  christos 			/* if we have zone information get rid of it */
   1554  1.1  christos 			if (ddns_cb->zone != NULL) {
   1555  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1556  1.1  christos 			}
   1557  1.1  christos 
   1558  1.1  christos 			ddns_cb->state = DDNS_STATE_ADD_PTR;
   1559  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1560  1.1  christos 
   1561  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1562  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1563  1.1  christos 				return;
   1564  1.1  christos 			}
   1565  1.1  christos 		}
   1566  1.1  christos 		break;
   1567  1.1  christos 
   1568  1.1  christos 	case DNS_R_YXDOMAIN:
   1569  1.1  christos 		/* we can reuse the zone information */
   1570  1.1  christos 		ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
   1571  1.1  christos 		ddns_cb->cur_func = ddns_fwd_srv_add2;
   1572  1.1  christos 
   1573  1.1  christos 		result = ddns_modify_fwd(ddns_cb, MDL);
   1574  1.1  christos 		if (result == ISC_R_SUCCESS) {
   1575  1.1  christos 			return;
   1576  1.1  christos 		}
   1577  1.1  christos 		break;
   1578  1.1  christos 	default:
   1579  1.1  christos 		log_error ("Unable to add forward map from %.*s to %s: %s",
   1580  1.1  christos 			   (int)ddns_cb->fwd_name.len,
   1581  1.1  christos 			   (const char *)ddns_cb->fwd_name.data,
   1582  1.1  christos 			   ddns_address,
   1583  1.1  christos 			   isc_result_totext (eresult));
   1584  1.1  christos 		break;
   1585  1.1  christos 	}
   1586  1.1  christos 
   1587  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1588  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1589  1.1  christos 	/*
   1590  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1591  1.1  christos 	 * the current state as the prerequisites for the first message
   1592  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1593  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1594  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1595  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1596  1.1  christos 	 * here rather than immediately after the call to update the lease
   1597  1.1  christos 	 * text in order to save any previously written data.
   1598  1.1  christos 	 */
   1599  1.1  christos 	commit_leases();
   1600  1.1  christos 	return;
   1601  1.1  christos }
   1602  1.1  christos 
   1603  1.1  christos /*
   1604  1.1  christos  * This action routine is invoked after the DSMM third add stage is
   1605  1.1  christos  * attempted.  If we succeeded we attempt to update the reverse DNS,
   1606  1.1  christos  * if not we cleanup and leave.
   1607  1.1  christos  */
   1608  1.1  christos void
   1609  1.1  christos ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb,
   1610  1.1  christos 		  isc_result_t    eresult)
   1611  1.1  christos {
   1612  1.1  christos 	isc_result_t result;
   1613  1.1  christos 	const char *logstr = NULL;
   1614  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN+1];
   1615  1.1  christos 
   1616  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1617  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_add3: %s eresult: %d",
   1618  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1619  1.1  christos #endif
   1620  1.1  christos 
   1621  1.1  christos 	/* Construct a printable form of the address for logging */
   1622  1.1  christos 	memset(ddns_address, 0x0, sizeof(ddns_address));
   1623  1.1  christos 	strncpy(ddns_address, piaddr(ddns_cb->address),
   1624  1.1  christos                 sizeof(ddns_address) - 1);
   1625  1.1  christos 
   1626  1.1  christos 	switch(eresult) {
   1627  1.1  christos 	case ISC_R_SUCCESS:
   1628  1.1  christos 		log_info("Added new forward map from %.*s to %s",
   1629  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1630  1.1  christos 			 (const char *)ddns_cb->fwd_name.data,
   1631  1.1  christos 			 ddns_address);
   1632  1.1  christos 
   1633  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1634  1.1  christos 
   1635  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1636  1.1  christos 			/* if we have zone information get rid of it */
   1637  1.1  christos 			if (ddns_cb->zone != NULL) {
   1638  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1639  1.1  christos 			}
   1640  1.1  christos 
   1641  1.1  christos 			ddns_cb->state = DDNS_STATE_ADD_PTR;
   1642  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1643  1.1  christos 
   1644  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1645  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1646  1.1  christos 				return;
   1647  1.1  christos 			}
   1648  1.1  christos 		}
   1649  1.1  christos 		break;
   1650  1.1  christos 
   1651  1.1  christos 	case DNS_R_YXRRSET:
   1652  1.1  christos 		logstr = "an entry that is either static or "
   1653  1.1  christos 			 "owned by another client exists.";
   1654  1.1  christos 		break;
   1655  1.1  christos 
   1656  1.1  christos 	case DNS_R_NXRRSET:
   1657  1.1  christos 		logstr = "static entry of the other protocol type exists.";
   1658  1.1  christos 		break;
   1659  1.1  christos 
   1660  1.1  christos 	default:
   1661  1.1  christos 		logstr = isc_result_totext(eresult);
   1662  1.1  christos 		break;
   1663  1.1  christos 	}
   1664  1.1  christos 
   1665  1.1  christos 	if (logstr != NULL) {
   1666  1.1  christos 		log_error("Forward map from %.*s to %s FAILED: %s",
   1667  1.1  christos 			  (int)ddns_cb->fwd_name.len,
   1668  1.1  christos 			  (const char *)ddns_cb->fwd_name.data,
   1669  1.1  christos 			  ddns_address, logstr);
   1670  1.1  christos 	}
   1671  1.1  christos 
   1672  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1673  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1674  1.1  christos 	/*
   1675  1.1  christos 	 * A single DDNS operation may require several calls depending on
   1676  1.1  christos 	 * the current state as the prerequisites for the first message
   1677  1.1  christos 	 * may not succeed requiring a second operation and potentially
   1678  1.1  christos 	 * a ptr operation after that.  The commit_leases operation is
   1679  1.1  christos 	 * invoked at the end of this set of operations in order to require
   1680  1.1  christos 	 * a single write for all of the changes.  We call commit_leases
   1681  1.1  christos 	 * here rather than immediately after the call to update the lease
   1682  1.1  christos 	 * text in order to save any previously written data.
   1683  1.1  christos 	 */
   1684  1.1  christos 	commit_leases();
   1685  1.1  christos 	return;
   1686  1.1  christos }
   1687  1.1  christos 
   1688  1.1  christos static void
   1689  1.1  christos ddns_fwd_srv_connector(struct lease          *lease,
   1690  1.1  christos 		       struct iasubopt       *lease6,
   1691  1.1  christos 		       struct binding_scope **inscope,
   1692  1.1  christos 		       dhcp_ddns_cb_t        *ddns_cb,
   1693  1.1  christos 		       isc_result_t           eresult)
   1694  1.1  christos {
   1695  1.1  christos 	isc_result_t result = ISC_R_FAILURE;
   1696  1.1  christos 
   1697  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1698  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_connector: %s eresult: %d",
   1699  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1700  1.1  christos #endif
   1701  1.1  christos 
   1702  1.1  christos 	if (ddns_cb == NULL) {
   1703  1.1  christos 		/* nothing to do */
   1704  1.1  christos 		return;
   1705  1.1  christos 	}
   1706  1.1  christos 
   1707  1.1  christos 	if (eresult == ISC_R_SUCCESS) {
   1708  1.1  christos 		/*
   1709  1.1  christos 		 * If we have updates dispatch as appropriate,
   1710  1.1  christos 		 * if not do FQDN binding if desired.
   1711  1.1  christos 		 */
   1712  1.1  christos 
   1713  1.1  christos 		if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
   1714  1.1  christos 			ddns_cb->state    = DDNS_STATE_ADD_FW_NXDOMAIN;
   1715  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_add1;
   1716  1.1  christos 			result = ddns_modify_fwd(ddns_cb, MDL);
   1717  1.1  christos 		} else if ((ddns_cb->flags & DDNS_UPDATE_PTR) &&
   1718  1.1  christos 			 (ddns_cb->rev_name.len != 0)) {
   1719  1.1  christos 			ddns_cb->state    = DDNS_STATE_ADD_PTR;
   1720  1.1  christos 			ddns_cb->cur_func = ddns_ptr_add;
   1721  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1722  1.1  christos 		} else {
   1723  1.1  christos 			ddns_update_lease_text(ddns_cb, inscope);
   1724  1.1  christos 		}
   1725  1.1  christos 	}
   1726  1.1  christos 
   1727  1.1  christos 
   1728  1.1  christos 	if (result == ISC_R_SUCCESS) {
   1729  1.1  christos 		ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL);
   1730  1.1  christos 	} else {
   1731  1.1  christos 		destroy_ddns_cb(ddns_cb, MDL);
   1732  1.1  christos 	}
   1733  1.1  christos 
   1734  1.1  christos 	return;
   1735  1.1  christos }
   1736  1.1  christos 
   1737  1.1  christos /*
   1738  1.1  christos  * If the first query fails, the updater MUST NOT delete the DNS name.  It
   1739  1.1  christos  * may be that the host whose lease on the server has expired has moved
   1740  1.1  christos  * to another network and obtained a lease from a different server,
   1741  1.1  christos  * which has caused the client's A RR to be replaced. It may also be
   1742  1.1  christos  * that some other client has been configured with a name that matches
   1743  1.1  christos  * the name of the DHCP client, and the policy was that the last client
   1744  1.1  christos  * to specify the name would get the name.  In this case, the DHCID RR
   1745  1.1  christos  * will no longer match the updater's notion of the client-identity of
   1746  1.1  christos  * the host pointed to by the DNS name.
   1747  1.1  christos  *   -- "Interaction between DHCP and DNS"
   1748  1.1  christos  */
   1749  1.1  christos 
   1750  1.2  christos static void
   1751  1.1  christos ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb,
   1752  1.1  christos 		  isc_result_t    eresult)
   1753  1.1  christos {
   1754  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1755  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_rem2: %s eresult: %d",
   1756  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1757  1.1  christos #endif
   1758  1.1  christos 
   1759  1.1  christos 	/*
   1760  1.1  christos 	 * To get here we have already managed to remove the A/AAAA
   1761  1.1  christos 	 * record and are trying to remove the DHCID/TXT record as well.
   1762  1.1  christos 	 * On success (removed DHCID/TXT) or YXRRSET (DHCID/TXT still in
   1763  1.1  christos 	 * use by something else) we clean up the lease.
   1764  1.1  christos 	 * On some other error we don't clean up the lease and hope that
   1765  1.1  christos 	 * if we try this again it will work.  An example would be if we
   1766  1.1  christos 	 * got a timeout as the DNS server halted between the first and
   1767  1.1  christos 	 * second steps.  The DNS server would still have the DHCID/TXT
   1768  1.1  christos 	 * and we would like to remove that in the future.
   1769  1.1  christos 	 *
   1770  1.1  christos 	 * On success set the EXECUTE_NEXT flag which triggers any
   1771  1.1  christos 	 * add that is next in the chain.
   1772  1.1  christos 	 */
   1773  1.1  christos 	if ((eresult == ISC_R_SUCCESS) ||
   1774  1.1  christos 	    (eresult == DNS_R_YXRRSET))  {
   1775  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1776  1.1  christos 		eresult = ISC_R_SUCCESS;
   1777  1.1  christos 	}
   1778  1.1  christos 
   1779  1.1  christos 	/* Do the next operation */
   1780  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1781  1.1  christos 		/* if we have zone information get rid of it */
   1782  1.1  christos 		if (ddns_cb->zone != NULL) {
   1783  1.1  christos 			ddns_cb_forget_zone(ddns_cb);
   1784  1.1  christos 		}
   1785  1.1  christos 
   1786  1.1  christos 		ddns_cb->state = DDNS_STATE_REM_PTR;
   1787  1.1  christos 		ddns_cb->cur_func = ddns_ptr_remove;
   1788  1.1  christos 		if (eresult == ISC_R_SUCCESS)
   1789  1.1  christos 			ddns_cb->flags |= DDNS_EXECUTE_NEXT;
   1790  1.1  christos 
   1791  1.1  christos 		eresult = ddns_modify_ptr(ddns_cb, MDL);
   1792  1.1  christos 		if (eresult == ISC_R_SUCCESS) {
   1793  1.1  christos 			return;
   1794  1.1  christos 		}
   1795  1.1  christos 	}
   1796  1.1  christos 
   1797  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1798  1.1  christos 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
   1799  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1800  1.1  christos 	return;
   1801  1.1  christos }
   1802  1.1  christos 
   1803  1.1  christos 
   1804  1.1  christos /*
   1805  1.1  christos  * First action routine when trying to remove a fwd
   1806  1.1  christos  * this will be called after the ddns queries have completed
   1807  1.1  christos  * if we succeeded in removing the fwd we go to the next step (if any)
   1808  1.1  christos  * if not we cleanup and leave.
   1809  1.1  christos  */
   1810  1.1  christos 
   1811  1.2  christos static void
   1812  1.1  christos ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb,
   1813  1.1  christos 		  isc_result_t    eresult)
   1814  1.1  christos {
   1815  1.1  christos 	isc_result_t result = eresult;
   1816  1.1  christos 	char ddns_address[MAX_ADDRESS_STRING_LEN];
   1817  1.1  christos 
   1818  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1819  1.1  christos 	log_info ("DDNS: ddns_fwd_srv_rem1: %s eresult: %d",
   1820  1.1  christos 		  dump_ddns_cb(ddns_cb), eresult);
   1821  1.1  christos #endif
   1822  1.1  christos 
   1823  1.1  christos 	switch(eresult) {
   1824  1.1  christos 	case ISC_R_SUCCESS:
   1825  1.1  christos 		/* Construct a printable form of the address for logging */
   1826  1.1  christos 		strcpy(ddns_address, piaddr(ddns_cb->address));
   1827  1.1  christos 		log_info("Removed forward map from %.*s to %s",
   1828  1.1  christos 			 (int)ddns_cb->fwd_name.len,
   1829  1.1  christos 			 (const char*)ddns_cb->fwd_name.data,
   1830  1.1  christos 			 ddns_address);
   1831  1.1  christos 
   1832  1.1  christos 		/* Do the second step of the FWD removal */
   1833  1.1  christos 		ddns_cb->state    = DDNS_STATE_REM_FW_NXRR;
   1834  1.1  christos 		ddns_cb->cur_func = ddns_fwd_srv_rem2;
   1835  1.1  christos 		result = ddns_modify_fwd(ddns_cb, MDL);
   1836  1.1  christos 		if (result == ISC_R_SUCCESS) {
   1837  1.1  christos 			return;
   1838  1.1  christos 		}
   1839  1.1  christos 		break;
   1840  1.1  christos 
   1841  1.1  christos 	case DNS_R_NXRRSET:
   1842  1.1  christos 	case DNS_R_NXDOMAIN:
   1843  1.1  christos 		/* A result of not found means rem1 did not find a guard of
   1844  1.1  christos 		 * our * type. From this we assume either there are no address
   1845  1.1  christos 		 * record(s) of our type to delete or they are unguarded and
   1846  1.1  christos 		 * therefore presumed to be static. Either way, we're done
   1847  1.1  christos 		 * unless we're in DSMM and ddns-other-guard-is-dynamic is on.
   1848  1.1  christos 		 * In which case we need to see if a guard of the other type
   1849  1.1  christos 		 * exists, which permits us to delete any address records of
   1850  1.1  christos 		 * our type. */
   1851  1.1  christos 		#define DSMM_OGD (DDNS_DUAL_STACK_MIXED_MODE | \
   1852  1.1  christos 				  DDNS_OTHER_GUARD_IS_DYNAMIC)
   1853  1.1  christos 		if ((ddns_cb->flags & DSMM_OGD) == DSMM_OGD) {
   1854  1.1  christos 			ddns_cb->state    = DDNS_STATE_REM_FW_DSMM_OTHER;
   1855  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_rem2;
   1856  1.1  christos 			result = ddns_modify_fwd(ddns_cb, MDL);
   1857  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1858  1.1  christos 				return;
   1859  1.1  christos 			}
   1860  1.1  christos 			break;
   1861  1.1  christos 		}
   1862  1.1  christos 
   1863  1.1  christos 		ddns_update_lease_text(ddns_cb, NULL);
   1864  1.1  christos 
   1865  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1866  1.1  christos 		log_info("DDNS: no forward map to remove. %p", ddns_cb);
   1867  1.1  christos #endif
   1868  1.1  christos 		/* Trigger the add operation */
   1869  1.1  christos 		eresult = ISC_R_SUCCESS;
   1870  1.1  christos 
   1871  1.1  christos 		/* Fall through */
   1872  1.1  christos 	default:
   1873  1.1  christos 
   1874  1.1  christos 		/* We do the remove operation in most cases
   1875  1.1  christos 		 * but we don't want to continue with adding a forward
   1876  1.1  christos 		 * record if the forward removal had issues so we
   1877  1.1  christos 		 * check the eresult and set the EXECUTE_NEXT flag on
   1878  1.1  christos 		 * success.
   1879  1.1  christos 		 */
   1880  1.1  christos 
   1881  1.1  christos 		/* Do the remove operation */
   1882  1.1  christos 		if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   1883  1.1  christos 			/* if we have zone information get rid of it */
   1884  1.1  christos 			if (ddns_cb->zone != NULL) {
   1885  1.1  christos 				ddns_cb_forget_zone(ddns_cb);
   1886  1.1  christos 			}
   1887  1.1  christos 
   1888  1.1  christos 			ddns_cb->state    = DDNS_STATE_REM_PTR;
   1889  1.1  christos 			ddns_cb->cur_func = ddns_ptr_remove;
   1890  1.1  christos 			if (eresult == ISC_R_SUCCESS)
   1891  1.1  christos 				ddns_cb->flags |= DDNS_EXECUTE_NEXT;
   1892  1.1  christos 
   1893  1.1  christos 			result = ddns_modify_ptr(ddns_cb, MDL);
   1894  1.1  christos 			if (result == ISC_R_SUCCESS) {
   1895  1.1  christos 				return;
   1896  1.1  christos 			}
   1897  1.1  christos 		}
   1898  1.1  christos 		break;
   1899  1.1  christos 	}
   1900  1.1  christos 
   1901  1.1  christos 	ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
   1902  1.1  christos 	ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
   1903  1.1  christos 	destroy_ddns_cb(ddns_cb, MDL);
   1904  1.1  christos }
   1905  1.1  christos 
   1906  1.1  christos /*%<
   1907  1.1  christos  * Remove relevant entries from DNS.
   1908  1.1  christos  *
   1909  1.1  christos  * \li lease  - lease to start with if this is for v4
   1910  1.1  christos  *
   1911  1.1  christos  * \li lease6 - lease to start with if this is for v6
   1912  1.1  christos  *
   1913  1.1  christos  * \li add_ddns_cb - control block for additional DDNS work.  This
   1914  1.1  christos  *     is used when the code is going to add a DDNS entry after removing
   1915  1.1  christos  *     the current entry.
   1916  1.1  christos  *
   1917  1.1  christos  * \li active - indication about the status of the lease. It is
   1918  1.1  christos  *     ISC_TRUE if the lease is still active, and FALSE if the lease
   1919  1.1  christos  *     is inactive.  This is used to indicate if the lease is inactive or going
   1920  1.1  christos  *     to inactive so we can avoid trying to update the lease with cb pointers
   1921  1.1  christos  *     and text information if it isn't useful.
   1922  1.1  christos  *
   1923  1.1  christos  * Returns
   1924  1.1  christos  * \li #ISC_R_FAILURE - badness occurred and we weren't able to do what was wanted
   1925  1.1  christos  * \li #ISC_R_SUCCESS - we were able to do stuff but it's in progress
   1926  1.1  christos  *
   1927  1.1  christos  * in both cases any additional block has been passed on to it's handler
   1928  1.1  christos  */
   1929  1.1  christos 
   1930  1.1  christos isc_result_t
   1931  1.1  christos ddns_removals(struct lease    *lease,
   1932  1.1  christos 	      struct iasubopt *lease6,
   1933  1.1  christos 	      dhcp_ddns_cb_t  *add_ddns_cb,
   1934  1.1  christos 	      isc_boolean_t    active)
   1935  1.1  christos {
   1936  1.1  christos 	isc_result_t rcode, execute_add = ISC_R_FAILURE;
   1937  1.1  christos 	struct binding_scope **scope = NULL;
   1938  1.1  christos 	isc_result_t result = ISC_R_FAILURE;
   1939  1.1  christos 	dhcp_ddns_cb_t        *ddns_cb = NULL;
   1940  1.1  christos 	struct data_string     leaseid;
   1941  1.1  christos 
   1942  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1943  1.1  christos 	log_info ("DDNS: ddns_removals: %s",
   1944  1.1  christos 		  dump_ddns_cb(add_ddns_cb));
   1945  1.1  christos #endif
   1946  1.1  christos 
   1947  1.1  christos 	/*
   1948  1.1  christos 	 * See if we need to cancel an outstanding request.  Mostly this is
   1949  1.1  christos 	 * used to handle the case where this routine is called twice for
   1950  1.1  christos 	 * the same release or abandon event.
   1951  1.1  christos 	 *
   1952  1.1  christos 	 * When called from the dns code as part of an update request
   1953  1.1  christos 	 * (add_ddns_cb != NULL) any outstanding requests will have already
   1954  1.1  christos 	 * been cancelled.
   1955  1.1  christos 	 *
   1956  1.1  christos 	 * If the new request is just a removal and we have an outstanding
   1957  1.1  christos 	 * request we have several options:
   1958  1.1  christos 	 *
   1959  1.1  christos 	 * - we are doing an update or we are doing a removal and the active
   1960  1.1  christos 	 * flag has changed from TRUE to FALSE.  In these cases we  need to
   1961  1.1  christos 	 * cancel the old request and start the new one.
   1962  1.1  christos 	 *
   1963  1.1  christos 	 * - other wise we are doing a removal with the active flag unchanged.
   1964  1.1  christos 	 * In this case we can let the current removal continue and do not need
   1965  1.1  christos 	 * to start a new one.  If the old request included an update to be
   1966  1.1  christos 	 * done after the removal we need to kill the update part of the
   1967  1.1  christos 	 * request.
   1968  1.1  christos 	 */
   1969  1.1  christos 
   1970  1.1  christos 	if (add_ddns_cb == NULL) {
   1971  1.1  christos 		if ((lease != NULL) && (lease->ddns_cb != NULL)) {
   1972  1.1  christos 			ddns_cb = lease->ddns_cb;
   1973  1.1  christos 
   1974  1.1  christos 			/*
   1975  1.1  christos 			 * Is the old request an update or did the
   1976  1.1  christos 			 * the active flag change?
   1977  1.1  christos 			 */
   1978  1.1  christos 			if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
   1979  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
   1980  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
   1981  1.1  christos 			    ((active == ISC_FALSE) &&
   1982  1.1  christos 			     ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
   1983  1.1  christos 				/* Cancel the current request */
   1984  1.1  christos 				ddns_cancel(lease->ddns_cb, MDL);
   1985  1.1  christos 				lease->ddns_cb = NULL;
   1986  1.1  christos 			} else {
   1987  1.1  christos 				/* Remvoval, check and remove updates */
   1988  1.1  christos 				if (ddns_cb->next_op != NULL) {
   1989  1.1  christos 					destroy_ddns_cb(ddns_cb->next_op, MDL);
   1990  1.1  christos 					ddns_cb->next_op = NULL;
   1991  1.1  christos 				}
   1992  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   1993  1.1  christos 				log_info("DDNS %s(%d): removal already in "
   1994  1.1  christos 					 "progress new ddns_cb=%p",
   1995  1.1  christos 					 MDL, ddns_cb);
   1996  1.1  christos #endif
   1997  1.1  christos 				return (ISC_R_SUCCESS);
   1998  1.1  christos 			}
   1999  1.1  christos 		} else if ((lease6 != NULL) && (lease6->ddns_cb != NULL)) {
   2000  1.1  christos 			ddns_cb = lease6->ddns_cb;
   2001  1.1  christos 
   2002  1.1  christos 			/*
   2003  1.1  christos 			 * Is the old request an update or did the
   2004  1.1  christos 			 * the active flag change?
   2005  1.1  christos 			 */
   2006  1.1  christos 			if (((ddns_cb->state == DDNS_STATE_ADD_PTR) ||
   2007  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) ||
   2008  1.1  christos 			     (ddns_cb->state == DDNS_STATE_ADD_FW_YXDHCID)) ||
   2009  1.1  christos 			    ((active == ISC_FALSE) &&
   2010  1.1  christos 			     ((ddns_cb->flags & DDNS_ACTIVE_LEASE) != 0))) {
   2011  1.1  christos 				/* Cancel the current request */
   2012  1.1  christos 				ddns_cancel(lease6->ddns_cb, MDL);
   2013  1.1  christos 				lease6->ddns_cb = NULL;
   2014  1.1  christos 			} else {
   2015  1.1  christos 				/* Remvoval, check and remove updates */
   2016  1.1  christos 				if (ddns_cb->next_op != NULL) {
   2017  1.1  christos 					destroy_ddns_cb(ddns_cb->next_op, MDL);
   2018  1.1  christos 					ddns_cb->next_op = NULL;
   2019  1.1  christos 				}
   2020  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2021  1.1  christos 				log_info("DDNS %s(%d): removal already in "
   2022  1.1  christos 					 "progress new ddns_cb=%p",
   2023  1.1  christos 					 MDL, ddns_cb);
   2024  1.1  christos #endif
   2025  1.1  christos 				return (ISC_R_SUCCESS);
   2026  1.1  christos 			}
   2027  1.1  christos 		}
   2028  1.1  christos 		ddns_cb = NULL;
   2029  1.1  christos 	}
   2030  1.1  christos 
   2031  1.1  christos 	/* allocate our control block */
   2032  1.1  christos 	ddns_cb = ddns_cb_alloc(MDL);
   2033  1.1  christos 	if (ddns_cb == NULL) {
   2034  1.1  christos 		goto cleanup;
   2035  1.1  christos 	}
   2036  1.1  christos 
   2037  1.1  christos 	/* Set the conflict detection flags based on global configuration */
   2038  1.1  christos 	copy_conflict_flags(&ddns_cb->flags, ddns_conflict_mask);
   2039  1.1  christos 
   2040  1.1  christos 	/*
   2041  1.1  christos 	 * For v4 we flag static leases so we don't try
   2042  1.1  christos 	 * and manipulate the lease later.  For v6 we don't
   2043  1.1  christos 	 * get static leases and don't need to flag them.
   2044  1.1  christos 	 */
   2045  1.1  christos 	if (lease != NULL) {
   2046  1.1  christos 		scope = &(lease->scope);
   2047  1.1  christos 		ddns_cb->address = lease->ip_addr;
   2048  1.1  christos 		if (lease->flags & STATIC_LEASE)
   2049  1.1  christos 			ddns_cb->flags |= DDNS_STATIC_LEASE;
   2050  1.1  christos 	} else if (lease6 != NULL) {
   2051  1.1  christos 		scope = &(lease6->scope);
   2052  1.1  christos 		memcpy(&ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
   2053  1.1  christos 		ddns_cb->address.len = 16;
   2054  1.1  christos 	} else
   2055  1.1  christos 		goto cleanup;
   2056  1.1  christos 
   2057  1.1  christos 	/*
   2058  1.1  christos 	 * Set the flag bit if the lease is active, that is it isn't
   2059  1.1  christos 	 * expired or released.  This is used to determine if we need
   2060  1.1  christos 	 * to update the scope information for both v4 and v6 and
   2061  1.1  christos 	 * the lease information for v6 when the response
   2062  1.1  christos 	 * from the DNS code is processed.
   2063  1.1  christos 	 */
   2064  1.1  christos 	if (active == ISC_TRUE) {
   2065  1.1  christos 		ddns_cb->flags |= DDNS_ACTIVE_LEASE;
   2066  1.1  christos 	}
   2067  1.1  christos 
   2068  1.1  christos 	/* No scope implies that DDNS has not been performed for this lease. */
   2069  1.1  christos 	if (*scope == NULL)
   2070  1.1  christos 		goto cleanup;
   2071  1.1  christos 
   2072  1.1  christos 	if ((ddns_update_style != DDNS_UPDATE_STYLE_STANDARD) &&
   2073  1.1  christos 	    (ddns_update_style != DDNS_UPDATE_STYLE_INTERIM))
   2074  1.1  christos 		goto cleanup;
   2075  1.1  christos 
   2076  1.1  christos 	/* Assume that we are removing both records */
   2077  1.1  christos 	ddns_cb->flags |= DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR;
   2078  1.1  christos 
   2079  1.1  christos 	/* and that we want to do the add call */
   2080  1.1  christos 	execute_add = ISC_R_SUCCESS;
   2081  1.1  christos 
   2082  1.1  christos 	/*
   2083  1.1  christos 	 * Look up stored names.
   2084  1.1  christos 	 */
   2085  1.1  christos 
   2086  1.1  christos 	/*
   2087  1.1  christos 	 * Find the fwd name and copy it to the control block.  If we don't
   2088  1.1  christos 	 * have it we can't delete the fwd record but we can still try to
   2089  1.1  christos 	 * remove the ptr record and cleanup the lease information if the
   2090  1.1  christos 	 * client did the fwd update.
   2091  1.1  christos 	 */
   2092  1.1  christos 	if (!find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-fwd-name")) {
   2093  1.1  christos 		/* don't try and delete the A, or do the add */
   2094  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
   2095  1.1  christos 		execute_add = ISC_R_FAILURE;
   2096  1.1  christos 
   2097  1.1  christos 		/* Check if client did update */
   2098  1.1  christos 		if (find_bound_string(&ddns_cb->fwd_name, *scope,
   2099  1.1  christos 				      "ddns-client-fqdn")) {
   2100  1.1  christos 			ddns_cb->flags |= DDNS_CLIENT_DID_UPDATE;
   2101  1.1  christos 		}
   2102  1.1  christos 	}
   2103  1.1  christos 
   2104  1.1  christos 	/*
   2105  1.1  christos 	 * Find the txt or dhcid tag and copy it to the control block. If we
   2106  1.1  christos 	 * don't have one this isn't an interim or standard record so we can't
   2107  1.1  christos 	 * delete the A record using this mechanism but we can delete the ptr
   2108  1.1  christos 	 * record. In this case we will attempt to do any requested next step.
   2109  1.1  christos 	 */
   2110  1.1  christos 	memset(&leaseid, 0, sizeof(leaseid));
   2111  1.1  christos 	if (find_bound_string (&leaseid, *scope, ddns_standard_tag)) {
   2112  1.1  christos 		/* We have a standard tag */
   2113  1.1  christos 		ddns_cb->lease_tag = ddns_standard_tag;
   2114  1.1  christos 		ddns_cb->dhcid_class = dns_rdatatype_dhcid;
   2115  1.1  christos 		ddns_cb->other_dhcid_class = dns_rdatatype_txt;
   2116  1.1  christos 		data_string_copy(&ddns_cb->dhcid, &leaseid, MDL);
   2117  1.1  christos 		data_string_forget(&leaseid, MDL);
   2118  1.1  christos 	} else 	if (find_bound_string (&leaseid, *scope, ddns_interim_tag)) {
   2119  1.1  christos 		/* we have an interim tag */
   2120  1.1  christos 		ddns_cb->lease_tag = ddns_interim_tag;
   2121  1.1  christos 		ddns_cb->dhcid_class = dns_rdatatype_txt;
   2122  1.1  christos 		ddns_cb->other_dhcid_class = dns_rdatatype_dhcid;
   2123  1.1  christos 		if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) !=
   2124  1.1  christos 		    ISC_R_SUCCESS) {
   2125  1.1  christos 			/* We couldn't convert the dhcid from the lease
   2126  1.1  christos 			 * version to the dns version.  We can't delete
   2127  1.1  christos 			 * the A record but can continue to the ptr
   2128  1.1  christos 			 */
   2129  1.1  christos 			ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
   2130  1.1  christos 		}
   2131  1.1  christos 		data_string_forget(&leaseid, MDL);
   2132  1.1  christos 	} else {
   2133  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
   2134  1.1  christos 	}
   2135  1.1  christos 
   2136  1.1  christos 	/*
   2137  1.1  christos 	 * Find the rev name and copy it to the control block.  If we don't
   2138  1.1  christos 	 * have it we can't get rid of it but we can try to remove the fwd
   2139  1.1  christos 	 * pointer if desired.
   2140  1.1  christos 	 */
   2141  1.1  christos 	if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) {
   2142  1.1  christos 		ddns_cb->flags &= ~DDNS_UPDATE_PTR;
   2143  1.1  christos 	}
   2144  1.1  christos 
   2145  1.1  christos 
   2146  1.1  christos 	/*
   2147  1.1  christos 	 * If we have a second control block for doing an add
   2148  1.1  christos 	 * after the remove finished attach it to our control block.
   2149  1.1  christos 	 */
   2150  1.1  christos 	ddns_cb->next_op = add_ddns_cb;
   2151  1.1  christos 
   2152  1.1  christos 	/*
   2153  1.1  christos 	 * Now that we've collected the information we can try to process it.
   2154  1.1  christos 	 * If necessary we call an appropriate routine to send a message and
   2155  1.1  christos 	 * provide it with an action routine to run on the control block given
   2156  1.1  christos 	 * the results of the message.  We have three entry points from here,
   2157  1.1  christos 	 * one for removing the A record, the next for removing the PTR and
   2158  1.1  christos 	 * the third for doing any requested add.
   2159  1.1  christos 	 */
   2160  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
   2161  1.1  christos 		if (ddns_cb->fwd_name.len != 0) {
   2162  1.1  christos 			ddns_cb->state    = DDNS_STATE_REM_FW_YXDHCID;
   2163  1.1  christos 			ddns_cb->cur_func = ddns_fwd_srv_rem1;
   2164  1.1  christos 
   2165  1.1  christos 			rcode = ddns_modify_fwd(ddns_cb, MDL);
   2166  1.1  christos 			if (rcode == ISC_R_SUCCESS) {
   2167  1.1  christos 				ddns_update_lease_ptr(lease, lease6, ddns_cb,
   2168  1.1  christos 						      ddns_cb, MDL);
   2169  1.1  christos 				return (ISC_R_SUCCESS);
   2170  1.1  christos 			}
   2171  1.1  christos 
   2172  1.1  christos 			/*
   2173  1.1  christos 			 * We weren't able to process the request tag the
   2174  1.1  christos 			 * add so we won't execute it.
   2175  1.1  christos 			 */
   2176  1.1  christos 			execute_add = ISC_R_FAILURE;
   2177  1.1  christos 			goto cleanup;
   2178  1.1  christos 		}
   2179  1.1  christos 		else {
   2180  1.1  christos 			/*remove info from scope */
   2181  1.1  christos 			unset(*scope, "ddns-fwd-name");
   2182  1.1  christos 			unset(*scope, ddns_cb->lease_tag);
   2183  1.1  christos 		}
   2184  1.1  christos 	}
   2185  1.1  christos 
   2186  1.1  christos 	if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
   2187  1.1  christos 		ddns_cb->state      = DDNS_STATE_REM_PTR;
   2188  1.1  christos 		ddns_cb->cur_func   = ddns_ptr_remove;
   2189  1.1  christos 		ddns_cb->flags      |= DDNS_EXECUTE_NEXT;
   2190  1.1  christos 
   2191  1.1  christos 		/*
   2192  1.1  christos 		 * if execute add isn't success remove the control block so
   2193  1.1  christos 		 * it won't be processed when the remove completes.  We
   2194  1.1  christos 		 * also arrange to clean it up and get rid of it.
   2195  1.1  christos 		 */
   2196  1.1  christos 		if (execute_add != ISC_R_SUCCESS) {
   2197  1.1  christos 		   	ddns_cb->next_op = NULL;
   2198  1.1  christos 			ddns_fwd_srv_connector(lease, lease6, scope,
   2199  1.1  christos 					       add_ddns_cb, execute_add);
   2200  1.1  christos 			add_ddns_cb = NULL;
   2201  1.1  christos 		}
   2202  1.1  christos 		else {
   2203  1.1  christos 			result = ISC_R_SUCCESS;
   2204  1.1  christos 		}
   2205  1.1  christos 
   2206  1.1  christos 		rcode = ddns_modify_ptr(ddns_cb, MDL);
   2207  1.1  christos 		if (rcode == ISC_R_SUCCESS) {
   2208  1.1  christos 			ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb,
   2209  1.1  christos 					      MDL);
   2210  1.1  christos 			return (result);
   2211  1.1  christos 		}
   2212  1.1  christos 
   2213  1.1  christos 		/* We weren't able to process the request tag the
   2214  1.1  christos 		 * add so we won't execute it */
   2215  1.1  christos 		execute_add = ISC_R_FAILURE;
   2216  1.1  christos 		goto cleanup;
   2217  1.1  christos 	}
   2218  1.1  christos 
   2219  1.1  christos  cleanup:
   2220  1.1  christos 	/*
   2221  1.1  christos 	 * We've gotten here because we didn't need to send a message or
   2222  1.1  christos 	 * we failed when trying to do so.  We send the additional cb
   2223  1.1  christos 	 * off to handle sending and/or cleanup and cleanup anything
   2224  1.1  christos 	 * we allocated here.
   2225  1.1  christos 	 */
   2226  1.1  christos 	ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add);
   2227  1.1  christos 	if (ddns_cb != NULL)
   2228  1.1  christos 		destroy_ddns_cb(ddns_cb, MDL);
   2229  1.1  christos 
   2230  1.1  christos 	return (result);
   2231  1.1  christos }
   2232  1.1  christos 
   2233  1.1  christos /* Convenience function for setting flag bits in a mask */
   2234  1.2  christos static void
   2235  1.2  christos set_flag (u_int16_t *flags,
   2236  1.1  christos 	       u_int16_t flag,
   2237  1.1  christos                u_int16_t value) {
   2238  1.1  christos 	if (flags) {
   2239  1.1  christos 		if (value) {
   2240  1.1  christos 			*flags |= flag;
   2241  1.1  christos 		} else {
   2242  1.1  christos 			*flags &= ~flag;
   2243  1.1  christos 		}
   2244  1.1  christos 	}
   2245  1.1  christos }
   2246  1.1  christos 
   2247  1.1  christos /*
   2248  1.1  christos  * Convenience function which replicates the conflict flags set in one
   2249  1.1  christos  * mask to another, while preserving all other flags.
   2250  1.1  christos  */
   2251  1.1  christos void copy_conflict_flags(u_int16_t *target,
   2252  1.1  christos 			 u_int16_t source)  {
   2253  1.1  christos 	if (target) {
   2254  1.1  christos 		/* Preserve non conflict flags */
   2255  1.1  christos 		*target &= ~CONFLICT_BITS;
   2256  1.1  christos 
   2257  1.1  christos 		/* Enable conflict flags per source */
   2258  1.1  christos 		*target |= source & CONFLICT_BITS;
   2259  1.1  christos 	}
   2260  1.1  christos }
   2261  1.1  christos 
   2262  1.1  christos /*
   2263  1.1  christos  * Given an option_state, create a mask of conflict detection flags based
   2264  1.1  christos  * on the appropriate configuration parameters within the option state.
   2265  1.1  christos  */
   2266  1.1  christos u_int16_t
   2267  1.1  christos get_conflict_mask(struct option_state *options) {
   2268  1.1  christos 
   2269  1.1  christos 	int ddns_update_conflict_detection = 1;  /* default on  */
   2270  1.1  christos         int ddns_dual_stack_mixed_mode = 0;	 /* default off */
   2271  1.1  christos         int ddns_guard_id_must_match = 1;	 /* default on  */
   2272  1.1  christos         int ddns_other_guard_is_dynamic = 0;	 /* default off */
   2273  1.1  christos 	struct option_cache *oc = NULL;
   2274  1.1  christos 
   2275  1.1  christos 	u_int16_t mask = 0;
   2276  1.1  christos 	oc = lookup_option(&server_universe, options, SV_DDNS_CONFLICT_DETECT);
   2277  1.1  christos 	if (oc) {
   2278  1.1  christos 		ddns_update_conflict_detection =
   2279  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2280  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2281  1.1  christos 	}
   2282  1.1  christos 
   2283  1.1  christos 	set_flag(&mask, DDNS_CONFLICT_DETECTION,
   2284  1.1  christos 		 ddns_update_conflict_detection);
   2285  1.1  christos 
   2286  1.1  christos 	if (!ddns_update_conflict_detection) {
   2287  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2288  1.1  christos 		log_info ("DDNS conflict detection: off");
   2289  1.1  christos #endif
   2290  1.1  christos 		/* Turn the rest of the conflict related flags off */
   2291  1.1  christos 		set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE, 0);
   2292  1.1  christos 		set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH, 0);
   2293  1.1  christos 		set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC, 0);
   2294  1.1  christos 		return (mask);
   2295  1.1  christos 	}
   2296  1.1  christos 
   2297  1.1  christos 	// Get the values
   2298  1.1  christos         oc = lookup_option(&server_universe, options,
   2299  1.1  christos                            SV_DDNS_DUAL_STACK_MIXED_MODE);
   2300  1.1  christos         if (oc) {
   2301  1.1  christos                 ddns_dual_stack_mixed_mode =
   2302  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2303  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2304  1.1  christos         }
   2305  1.1  christos 
   2306  1.1  christos         oc = lookup_option(&server_universe, options,
   2307  1.1  christos                            SV_DDNS_GUARD_ID_MUST_MATCH);
   2308  1.1  christos         if (oc) {
   2309  1.1  christos                 ddns_guard_id_must_match =
   2310  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2311  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2312  1.1  christos         }
   2313  1.1  christos 
   2314  1.1  christos         oc = lookup_option(&server_universe, options,
   2315  1.1  christos                            SV_DDNS_OTHER_GUARD_IS_DYNAMIC);
   2316  1.1  christos         if (oc) {
   2317  1.1  christos                 ddns_other_guard_is_dynamic =
   2318  1.1  christos 		evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options,
   2319  1.1  christos 					      NULL, &global_scope, oc, MDL);
   2320  1.1  christos         }
   2321  1.1  christos 
   2322  1.1  christos 	// Set the flags
   2323  1.1  christos 	set_flag(&mask, DDNS_DUAL_STACK_MIXED_MODE,
   2324  1.1  christos 		 ddns_dual_stack_mixed_mode);
   2325  1.1  christos 
   2326  1.1  christos 	set_flag(&mask, DDNS_GUARD_ID_MUST_MATCH,
   2327  1.1  christos 		 ddns_guard_id_must_match);
   2328  1.1  christos 
   2329  1.1  christos 	set_flag(&mask, DDNS_OTHER_GUARD_IS_DYNAMIC,
   2330  1.1  christos 		 ddns_other_guard_is_dynamic);
   2331  1.1  christos 
   2332  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2333  1.1  christos 	log_info ("DDNS conflict behavior:\n"
   2334  1.1  christos 		  "\tddns-update-style: %s\n"
   2335  1.1  christos 		  "\tupdate-conflict-detection: %d\n"
   2336  1.1  christos 		  "\tddns-dual-stack-mixed-mode: %d\n"
   2337  1.1  christos 		  "\tddns-guard-id-must-match %d\n"
   2338  1.1  christos 		  "\tddns-other-guard-is-dynamic: %d\n",
   2339  1.1  christos 		  ddns_styles_values[ddns_update_style].name,
   2340  1.1  christos 		  ddns_update_conflict_detection,
   2341  1.1  christos 		  ddns_dual_stack_mixed_mode,
   2342  1.1  christos 		  ddns_guard_id_must_match,
   2343  1.1  christos 		  ddns_other_guard_is_dynamic);
   2344  1.1  christos #endif
   2345  1.1  christos 	return (mask);
   2346  1.1  christos }
   2347  1.1  christos 
   2348  1.1  christos #if defined (DEBUG_DNS_UPDATES)
   2349  1.1  christos /* Type used for creating lists of function pointers and their names */
   2350  1.1  christos typedef struct {
   2351  1.1  christos 	void *ptr;
   2352  1.1  christos 	char *name;
   2353  1.1  christos } LabeledPtr;
   2354  1.1  christos 
   2355  1.1  christos /* Returns the name of the function referred to by the given address */
   2356  1.1  christos char*
   2357  1.1  christos dump_ddns_cb_func(void *func) {
   2358  1.1  christos 	static LabeledPtr funcs[] = {
   2359  1.1  christos 		{ ddns_ptr_add, "ddns_ptr_add" },
   2360  1.1  christos 		{ ddns_fwd_srv_add2, "ddns_fwd_srv_add2" },
   2361  1.1  christos 		{ ddns_fwd_srv_add1, "ddns_fwd_srv_add1" },
   2362  1.1  christos 		{ ddns_ptr_remove, "ddns_ptr_remove" },
   2363  1.1  christos 		{ ddns_fwd_srv_rem2, "ddns_fwd_srv_rem2" },
   2364  1.1  christos 		{ ddns_fwd_srv_rem1, "ddns_fwd_srv_rem1" },
   2365  1.1  christos 		{ ddns_fwd_srv_add3, "ddns_fwd_srv_adde" },
   2366  1.1  christos 		{ NULL, "unknown" }
   2367  1.1  christos 	};
   2368  1.1  christos 
   2369  1.1  christos 	LabeledPtr* lp = funcs;
   2370  1.1  christos 	if (!func) {
   2371  1.1  christos 		return ("<null>");
   2372  1.1  christos 	}
   2373  1.1  christos 
   2374  1.1  christos 	while ((lp->ptr) && (lp->ptr != func)) {
   2375  1.1  christos 		++lp;
   2376  1.1  christos 	}
   2377  1.1  christos 
   2378  1.1  christos 	return (lp->name);
   2379  1.1  christos }
   2380  1.1  christos 
   2381  1.1  christos /* Dumps basic control block info to the log */
   2382  1.1  christos char*
   2383  1.1  christos dump_ddns_cb (dhcp_ddns_cb_t *ddns_cb) {
   2384  1.1  christos 	static char output_buf[4096];
   2385  1.1  christos 	if (!ddns_cb) {
   2386  1.1  christos 		return ("<ddns_cb is null>");
   2387  1.1  christos 	}
   2388  1.1  christos 
   2389  1.1  christos 	sprintf (output_buf, "ddns_cb: %p flags: %x state: %s cur_func: %s",
   2390  1.1  christos 		ddns_cb, ddns_cb->flags,
   2391  1.1  christos 		ddns_state_name(ddns_cb->state),
   2392  1.1  christos 		dump_ddns_cb_func(ddns_cb->cur_func));
   2393  1.1  christos 
   2394  1.1  christos 	return(output_buf);
   2395  1.1  christos }
   2396  1.1  christos #endif /* DEBUG_DNS_UPDATES */
   2397  1.1  christos 
   2398  1.1  christos #endif /* NSUPDATE */
   2399