]> git.ipfire.org Git - thirdparty/dhcp.git/blobdiff - server/mdb.c
Update RELNOTES
[thirdparty/dhcp.git] / server / mdb.c
index 741fd9bcd1f5eee639478a416a5789bf12345ea7..b982cc16e51b6207b5277531be707c7630ab0be3 100644 (file)
    Server-specific in-memory database support. */
 
 /*
- * Copyright (c) 1996-2000 Internet Software Consortium.
- * All rights reserved.
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of The Internet Software Consortium nor the names
- *    of its contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
- * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ *   Internet Systems Consortium, Inc.
+ *   PO Box 360
+ *   Newmarket, NH 03857 USA
+ *   <info@isc.org>
+ *   https://www.isc.org/
  *
- * This software has been written for the Internet Software Consortium
- * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
- * To learn more about the Internet Software Consortium, see
- * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
- * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
- * ``http://www.nominum.com''.
  */
 
-#ifndef lint
-static char copyright[] =
-"$Id: mdb.c,v 1.62 2001/04/24 01:02:24 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
-#endif /* not lint */
-
 #include "dhcpd.h"
+#include "omapip/hash.h"
 
 struct subnet *subnets;
 struct shared_network *shared_networks;
-struct hash_table *host_hw_addr_hash;
-struct hash_table *host_uid_hash;
-struct hash_table *lease_uid_hash;
-struct hash_table *lease_ip_addr_hash;
-struct hash_table *lease_hw_addr_hash;
-struct hash_table *host_name_hash;
+host_hash_t *host_hw_addr_hash;
+host_hash_t *host_uid_hash;
+host_hash_t *host_name_hash;
+lease_id_hash_t *lease_uid_hash;
+lease_ip_hash_t *lease_ip_addr_hash;
+lease_id_hash_t *lease_hw_addr_hash;
+
+/*
+ * We allow users to specify any option as a host identifier.
+ *
+ * Any host is uniquely identified by the combination of
+ * option type & option data.
+ *
+ * We expect people will only use a few types of options as host
+ * identifier. Because of this, we store a list with an entry for
+ * each option type. Each of these has a hash table, which contains
+ * hash of the option data.
+ *
+ * For v6 we also include a relay count - this specifies which
+ * relay to check for the requested option.  As each different
+ * value of relays creates a new instance admins should use the
+ * same value across each option for all host-identifers.
+ * A value of 0 indicates that we aren't doing relay options
+ * and should simply look in the current option list.
+ */
+typedef struct host_id_info {
+       struct option *option;
+       host_hash_t *values_hash;
+       int relays;
+       struct host_id_info *next;
+} host_id_info_t;
+
+static host_id_info_t *host_id_info = NULL;
+
+int numclasseswritten;
+
+extern omapi_object_type_t *dhcp_type_host;
+
+isc_result_t enter_class(cd, dynamicp, commit)
+       struct class *cd;
+       int dynamicp;
+       int commit;
+{
+       if (!collections -> classes) {
+               /* A subclass with no parent is invalid. */
+               if (cd->name == NULL)
+                       return DHCP_R_INVALIDARG;
+
+               class_reference (&collections -> classes, cd, MDL);
+       } else if (cd->name != NULL) {  /* regular class */
+               struct class *c = 0;
+
+               if (find_class(&c, cd->name, MDL) != ISC_R_NOTFOUND) {
+                       class_dereference(&c, MDL);
+                       return ISC_R_EXISTS;
+               }
+
+               /* Find the tail. */
+               for (c = collections -> classes;
+                    c -> nic; c = c -> nic)
+                       /* nothing */ ;
+               class_reference (&c -> nic, cd, MDL);
+       }
+
+       if (dynamicp && commit) {
+               const char *name = cd->name;
+
+               if (name == NULL) {
+                       name = cd->superclass->name;
+               }
+
+               write_named_billing_class ((const unsigned char *)name, 0, cd);
+               if (!commit_leases ())
+                       return ISC_R_IOERROR;
+       }
+
+       return ISC_R_SUCCESS;
+}
+
 
-omapi_object_type_t *dhcp_type_host;
+/* Variable to check if we're starting the server.  The server will init as
+ * starting - but just to be safe start out as false to avoid triggering new
+ * special-case code
+ * XXX: There is actually a server_startup state...which is never entered...
+ */
+#define SS_NOSYNC      1
+#define SS_QFOLLOW     2
+static int server_starting = 0;
+
+static int find_uid_statement (struct executable_statement *esp,
+                              void *vp, int condp)
+{
+       struct executable_statement **evp = vp;
+
+       if (esp -> op == supersede_option_statement &&
+           esp -> data.option &&
+           (esp -> data.option -> option -> universe ==
+            &dhcp_universe) &&
+           (esp -> data.option -> option -> code ==
+            DHO_DHCP_CLIENT_IDENTIFIER)) {
+               if (condp) {
+                       log_error ("dhcp client identifier may not be %s",
+                                  "specified conditionally.");
+               } else if (!(*evp)) {
+                       executable_statement_reference (evp, esp, MDL);
+                       return 1;
+               } else {
+                       log_error ("only one dhcp client identifier may be %s",
+                                  "specified");
+               }
+       }
+       return 0;
+}
+
+
+static host_id_info_t *
+find_host_id_info(unsigned int option_code, int relays) {
+       host_id_info_t *p;
+
+       for (p = host_id_info; p != NULL; p = p->next) {
+               if ((p->option->code == option_code) &&
+                   (p->relays == relays)) {
+                       break;
+               }
+       }
+       return p;
+}
+
+/* Debugging code */
+#if 0
+isc_result_t
+print_host(const void *name, unsigned len, void *value) {
+       struct host_decl *h;
+       printf("--------------\n");
+       printf("name:'%s'\n", print_hex_1(len, name, 60));
+       printf("len:%d\n", len);
+       h = (struct host_decl *)value;
+       printf("host @%p is '%s'\n", h, h->name);
+       return ISC_R_SUCCESS;
+}
+
+void
+hash_print_hosts(struct hash_table *h) {
+       hash_foreach(h, print_host);
+       printf("--------------\n");
+}
+#endif /* 0 */
+
+void
+change_host_uid(struct host_decl *host, const char *uid, int len) {
+       /* XXX: should consolidate this type of code throughout */
+       if (host_uid_hash == NULL) {
+               if (!host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL)) {
+                       log_fatal("Can't allocate host/uid hash");
+               }
+       }
+
+       /*
+        * Remove the old entry, if one exists.
+        */
+       if (host->client_identifier.data != NULL) {
+               host_hash_delete(host_uid_hash,
+                                host->client_identifier.data,
+                                host->client_identifier.len,
+                                MDL);
+               data_string_forget(&host->client_identifier, MDL);
+       }
+
+       /*
+        * Set our new value.
+        */
+       memset(&host->client_identifier, 0, sizeof(host->client_identifier));
+       host->client_identifier.len = len;
+       if (!buffer_allocate(&host->client_identifier.buffer, len, MDL)) {
+               log_fatal("Can't allocate uid buffer");
+       }
+       host->client_identifier.data = host->client_identifier.buffer->data;
+       memcpy((char *)host->client_identifier.data, uid, len);
+
+       /*
+        * And add to hash.
+        */
+       host_hash_add(host_uid_hash, host->client_identifier.data,
+                     host->client_identifier.len, host, MDL);
+}
 
 isc_result_t enter_host (hd, dynamicp, commit)
        struct host_decl *hd;
@@ -67,12 +225,10 @@ isc_result_t enter_host (hd, dynamicp, commit)
        struct host_decl *hp = (struct host_decl *)0;
        struct host_decl *np = (struct host_decl *)0;
        struct executable_statement *esp;
+       host_id_info_t *h_id_info;
 
        if (!host_name_hash) {
-               host_name_hash =
-                       new_hash ((hash_reference)host_reference,
-                                 (hash_dereference)host_dereference, 0);
-               if (!host_name_hash)
+               if (!host_new_hash(&host_name_hash, HOST_HASH_SIZE, MDL))
                        log_fatal ("Can't allocate host name hash");
                host_hash_add (host_name_hash,
                               (unsigned char *)hd -> name,
@@ -89,8 +245,20 @@ isc_result_t enter_host (hd, dynamicp, commit)
                                          strlen (hd -> name), MDL);
                        /* If the old entry wasn't dynamic, then we
                           always have to keep the deletion. */
-                       if (!hp -> flags & HOST_DECL_DYNAMIC)
+                       if (hp -> flags & HOST_DECL_STATIC) {
                                hd -> flags |= HOST_DECL_STATIC;
+                       }
+                       host_dereference (&hp, MDL);
+               }
+
+               /* If we are updating an existing host declaration, we
+                  can just delete it and add it again. */
+               if (hp && hp == hd) {
+                       host_dereference (&hp, MDL);
+                       delete_host (hd, 0);
+                       if (!write_host (hd))
+                               return ISC_R_IOERROR;
+                       hd -> flags &= ~HOST_DECL_DELETED;
                }
 
                /* If there isn't already a host decl matching this
@@ -115,11 +283,8 @@ isc_result_t enter_host (hd, dynamicp, commit)
 
        if (hd -> interface.hlen) {
                if (!host_hw_addr_hash) {
-                       host_hw_addr_hash =
-                               new_hash ((hash_reference)host_reference,
-                                         (hash_dereference)host_dereference,
-                                         0);
-                       if (!host_hw_addr_hash)
+                       if (!host_new_hash(&host_hw_addr_hash,
+                                          HOST_HASH_SIZE, MDL))
                                log_fatal ("Can't allocate host/hw hash");
                } else {
                        /* If there isn't already a host decl matching this
@@ -145,20 +310,34 @@ isc_result_t enter_host (hd, dynamicp, commit)
        /* See if there's a statement that sets the client identifier.
           This is a kludge - the client identifier really shouldn't be
           set with an executable statement. */
-       for (esp = hd -> group -> statements; esp; esp = esp -> next) {
-               if (esp -> op == supersede_option_statement &&
-                   esp -> data.option &&
-                   (esp -> data.option -> option -> universe ==
-                    &dhcp_universe) &&
-                   (esp -> data.option -> option -> code ==
-                    DHO_DHCP_CLIENT_IDENTIFIER)) {
-                       evaluate_option_cache
-                               (&hd -> client_identifier, (struct packet *)0,
-                                (struct lease *)0, (struct client_state *)0,
-                                (struct option_state *)0,
-                                (struct option_state *)0, &global_scope,
-                                esp -> data.option, MDL);
-                       break;
+       esp = NULL;
+       if (executable_statement_foreach (hd->group->statements,
+                                         find_uid_statement, &esp, 0)) {
+               struct data_string cid;
+               memset(&cid, 0, sizeof(cid));
+               (void) evaluate_option_cache (&cid,
+                                             NULL, NULL, NULL, NULL, NULL,
+                                             &global_scope,
+                                             esp->data.option, MDL);
+
+               if (hd->client_identifier.len > 0 && cid.len > 0) {
+                       char uid_buf[256];
+                       char cid_buf[256];
+                       print_hex_or_string(hd->client_identifier.len,
+                                           hd->client_identifier.data,
+                                           sizeof(uid_buf) - 1, uid_buf);
+
+                       print_hex_or_string(cid.len, cid.data,
+                                           sizeof(cid_buf) - 1, cid_buf);
+
+                       log_error ("Warning, host declaration '%s'"
+                                  " already has uid '%s',"
+                                  " ignoring dhcp-client-identifier '%s'",
+                                  hd->name, uid_buf, cid_buf);
+
+                       data_string_forget(&cid, MDL);
+               } else {
+                       memcpy(&hd->client_identifier, &cid, sizeof(cid));
                }
        }
 
@@ -168,11 +347,8 @@ isc_result_t enter_host (hd, dynamicp, commit)
                /* If there's no uid hash, make one; otherwise, see if
                   there's already an entry in the hash for this host. */
                if (!host_uid_hash) {
-                       host_uid_hash =
-                               new_hash ((hash_reference)host_reference,
-                                         (hash_dereference)host_dereference,
-                                         0);
-                       if (!host_uid_hash)
+                       if (!host_new_hash(&host_uid_hash,
+                                          HOST_HASH_SIZE, MDL))
                                log_fatal ("Can't allocate host/uid hash");
 
                        host_hash_add (host_uid_hash,
@@ -208,6 +384,65 @@ isc_result_t enter_host (hd, dynamicp, commit)
                }
        }
 
+
+       /*
+        * If we use an option as our host identifier, record it here.
+        */
+       if (hd->host_id_option != NULL) {
+               /*
+                * Look for the host identifier information for this option,
+                * and create a new entry if there is none.
+                */
+               h_id_info = find_host_id_info(hd->host_id_option->code,
+                                             hd->relays);
+               if (h_id_info == NULL) {
+                       h_id_info = dmalloc(sizeof(*h_id_info), MDL);
+                       if (h_id_info == NULL) {
+                               log_fatal("No memory for host-identifier "
+                                         "option information.");
+                       }
+                       option_reference(&h_id_info->option,
+                                        hd->host_id_option, MDL);
+                       if (!host_new_hash(&h_id_info->values_hash,
+                                          HOST_HASH_SIZE, MDL)) {
+                               log_fatal("No memory for host-identifier "
+                                         "option hash.");
+                       }
+                       h_id_info->relays = hd->relays;
+                       h_id_info->next = host_id_info;
+                       host_id_info = h_id_info;
+               }
+
+               if (host_hash_lookup(&hp, h_id_info->values_hash,
+                                    hd->host_id.data, hd->host_id.len, MDL)) {
+                       /*
+                        * If this option is already present, then add
+                        * this host to the list in n_ipaddr, unless
+                        * we have already done so previously.
+                        *
+                        * XXXSK: This seems scary to me, but I don't
+                        *        fully understand how these are used.
+                        *        Shouldn't there be multiple lists, or
+                        *        maybe we should just forbid duplicates?
+                        */
+                       if (np == NULL) {
+                               np = hp;
+                               while (np->n_ipaddr != NULL) {
+                                       np = np->n_ipaddr;
+                               }
+                               if (hd != np) {
+                                       host_reference(&np->n_ipaddr, hd, MDL);
+                               }
+                       }
+                       host_dereference(&hp, MDL);
+               } else {
+                       host_hash_add(h_id_info->values_hash,
+                                     hd->host_id.data,
+                                     hd->host_id.len,
+                                     hd, MDL);
+               }
+       }
+
        if (dynamicp && commit) {
                if (!write_host (hd))
                        return ISC_R_IOERROR;
@@ -218,6 +453,39 @@ isc_result_t enter_host (hd, dynamicp, commit)
        return ISC_R_SUCCESS;
 }
 
+
+isc_result_t delete_class (cp, commit)
+       struct class *cp;
+       int commit;
+{
+       cp->flags |= CLASS_DECL_DELETED;
+
+       /* do the write first as we won't be leaving it in any data
+          structures, unlike the host objects */
+
+       if (commit) {
+               write_named_billing_class ((unsigned char *)cp->name, 0, cp);
+               if (!commit_leases ())
+                       return ISC_R_IOERROR;
+       }
+
+       /*
+        * If this is a subclass remove it from the class's hash table
+        */
+       if (cp->superclass) {
+               class_hash_delete(cp->superclass->hash,
+                                 (const char *)cp->hash_string.data,
+                                 cp->hash_string.len,
+                                 MDL);
+       }
+
+       /* remove from collections */
+       unlink_class(&cp);
+
+       return ISC_R_SUCCESS;
+}
+
+
 isc_result_t delete_host (hd, commit)
        struct host_decl *hd;
        int commit;
@@ -225,7 +493,6 @@ isc_result_t delete_host (hd, commit)
        struct host_decl *hp = (struct host_decl *)0;
        struct host_decl *np = (struct host_decl *)0;
        struct host_decl *foo;
-       struct executable_statement *esp;
        int hw_head = 0, uid_head = 1;
 
        /* Don't need to do it twice. */
@@ -238,25 +505,37 @@ isc_result_t delete_host (hd, commit)
        if (hd -> interface.hlen) {
            if (host_hw_addr_hash) {
                if (host_hash_lookup (&hp, host_hw_addr_hash,
-                       hd -> interface.hbuf,
-                       hd -> interface.hlen, MDL)) {
+                                     hd -> interface.hbuf,
+                                     hd -> interface.hlen, MDL)) {
                    if (hp == hd) {
                        host_hash_delete (host_hw_addr_hash,
                                          hd -> interface.hbuf,
                                          hd -> interface.hlen, MDL);
                        hw_head = 1;
                    } else {
-                       for (foo = hp; foo; foo = foo -> n_ipaddr) {
+                       np = (struct host_decl *)0;
+                       foo = (struct host_decl *)0;
+                       host_reference (&foo, hp, MDL);
+                       while (foo) {
                            if (foo == hd)
                                    break;
-                           np = foo;
+                           if (np)
+                                   host_dereference (&np, MDL);
+                           host_reference (&np, foo, MDL);
+                           host_dereference (&foo, MDL);
+                           if (np -> n_ipaddr)
+                                   host_reference (&foo, np -> n_ipaddr, MDL);
                        }
+
                        if (foo) {
                            host_dereference (&np -> n_ipaddr, MDL);
                            if (hd -> n_ipaddr)
                                host_reference (&np -> n_ipaddr,
                                                hd -> n_ipaddr, MDL);
+                           host_dereference (&foo, MDL);
                        }
+                       if (np)
+                               host_dereference (&np, MDL);
                    }
                    host_dereference (&hp, MDL);
                }
@@ -276,23 +555,40 @@ isc_result_t delete_host (hd, commit)
                                          hd -> client_identifier.len, MDL);
                        uid_head = 1;
                    } else {
-                       for (foo = hp; foo; foo = foo -> n_ipaddr) {
+                       np = (struct host_decl *)0;
+                       foo = (struct host_decl *)0;
+                       host_reference (&foo, hp, MDL);
+                       while (foo) {
                            if (foo == hd)
-                               break;
-                           np = foo;
+                                   break;
+                           if (np)
+                               host_dereference (&np, MDL);
+                           host_reference (&np, foo, MDL);
+                           host_dereference (&foo, MDL);
+                           if (np -> n_ipaddr)
+                                   host_reference (&foo, np -> n_ipaddr, MDL);
                        }
+
                        if (foo) {
-                           if (np -> n_ipaddr)
-                               host_dereference (&np -> n_ipaddr, MDL);
-                           host_reference (&np -> n_ipaddr,
-                                           hd -> n_ipaddr, MDL);
+                           host_dereference (&np -> n_ipaddr, MDL);
+                           if (hd -> n_ipaddr)
+                               host_reference (&np -> n_ipaddr,
+                                               hd -> n_ipaddr, MDL);
+                           host_dereference (&foo, MDL);
                        }
+                       if (np)
+                               host_dereference (&np, MDL);
                    }
                    host_dereference (&hp, MDL);
                }
            }
        }
 
+       if (hd->host_id_option != NULL) {
+               option_dereference(&hd->host_id_option, MDL);
+               data_string_forget(&hd->host_id, MDL);
+       }
+
        if (hd -> n_ipaddr) {
                if (uid_head && hd -> n_ipaddr -> client_identifier.len) {
                        host_hash_add
@@ -336,8 +632,13 @@ int find_hosts_by_haddr (struct host_decl **hp, int htype,
                         const unsigned char *haddr, unsigned hlen,
                         const char *file, int line)
 {
-       struct host_decl *foo;
        struct hardware h;
+#if defined(LDAP_CONFIGURATION)
+       int ret;
+
+       if ((ret = find_haddr_in_ldap (hp, htype, hlen, haddr, file, line)))
+               return ret;
+#endif
 
        h.hlen = hlen + 1;
        h.hbuf [0] = htype;
@@ -354,6 +655,75 @@ int find_hosts_by_uid (struct host_decl **hp,
        return host_hash_lookup (hp, host_uid_hash, data, len, file, line);
 }
 
+int
+find_hosts_by_option(struct host_decl **hp,
+                    struct packet *packet,
+                    struct option_state *opt_state,
+                    const char *file, int line) {
+       host_id_info_t *p;
+       struct option_cache *oc;
+       struct data_string data;
+       int found;
+       struct packet *relay_packet;
+       struct option_state *relay_state;
+
+#if defined(LDAP_CONFIGURATION)
+       if ((found = find_client_in_ldap (hp, packet, opt_state, file, line)))
+               return found;
+#endif
+
+       for (p = host_id_info; p != NULL; p = p->next) {
+               relay_packet = packet;
+               relay_state = opt_state;
+
+               /* If this option block is for a relay (relays != 0)
+                * and we are processing the main options and not
+                * options from the IA (packet->options == opt_state)
+                * try to find the proper relay
+                */
+               if ((p->relays != 0) && (packet->options == opt_state)) {
+                       int i = p->relays;
+                       while ((i != 0) &&
+                              (relay_packet->dhcpv6_container_packet != NULL)) {
+                               relay_packet =
+                                       relay_packet->dhcpv6_container_packet;
+                               i--;
+                       }
+                       /* We wanted a specific relay but were
+                        * unable to find it */
+                       if ((p->relays <= MAX_V6RELAY_HOPS) && (i != 0))
+                               continue;
+
+                       relay_state = relay_packet->options;
+               }
+
+               oc = lookup_option(p->option->universe,
+                                  relay_state, p->option->code);
+               if (oc != NULL) {
+                       memset(&data, 0, sizeof(data));
+
+                       if (!evaluate_option_cache(&data, relay_packet, NULL,
+                                                  NULL, relay_state, NULL,
+                                                  &global_scope, oc,
+                                                  MDL)) {
+                               log_error("Error evaluating option cache");
+                               return 0;
+                       }
+
+                       found = host_hash_lookup(hp, p->values_hash,
+                                                data.data, data.len,
+                                                file, line);
+
+                       data_string_forget(&data, MDL);
+
+                       if (found) {
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
 /* More than one host_decl can be returned by find_hosts_by_haddr or
    find_hosts_by_uid, and each host_decl can have multiple addresses.
    Loop through the list of hosts, and then for each host, through the
@@ -366,7 +736,6 @@ int find_host_for_network (struct subnet **sp, struct host_decl **host,
                           struct iaddr *addr, struct shared_network *share)
 {
        int i;
-       struct subnet *subnet;
        struct iaddr ip_address;
        struct host_decl *hp;
        struct data_string fixed_addr;
@@ -409,18 +778,24 @@ int find_host_for_network (struct subnet **sp, struct host_decl **host,
        return 0;
 }
 
-void new_address_range (low, high, subnet, pool)
+void new_address_range (cfile, low, high, subnet, pool, lpchain)
+       struct parse *cfile;
        struct iaddr low, high;
        struct subnet *subnet;
        struct pool *pool;
+       struct lease **lpchain;
 {
-       struct lease *address_range, *lp, *plp;
-       struct iaddr net;
-       unsigned min, max, i;
+#if defined(COMPACT_LEASES)
+       struct lease *address_range;
+       unsigned s;
+#endif
+       unsigned min, max, i, num_addrs;
        char lowbuf [16], highbuf [16], netbuf [16];
        struct shared_network *share = subnet -> shared_network;
-       isc_result_t status;
        struct lease *lt = (struct lease *)0;
+#if !defined(COMPACT_LEASES)
+       isc_result_t status;
+#endif
 
        /* All subnets should have attached shared network structures. */
        if (!share) {
@@ -431,44 +806,33 @@ void new_address_range (low, high, subnet, pool)
 
        /* Initialize the hash table if it hasn't been done yet. */
        if (!lease_uid_hash) {
-               lease_uid_hash =
-                       new_hash ((hash_reference)lease_reference,
-                                 (hash_dereference)lease_dereference, 0);
-               if (!lease_uid_hash)
+               if (!lease_id_new_hash(&lease_uid_hash, LEASE_HASH_SIZE, MDL))
                        log_fatal ("Can't allocate lease/uid hash");
        }
        if (!lease_ip_addr_hash) {
-               lease_ip_addr_hash =
-                       new_hash ((hash_reference)lease_reference,
-                                 (hash_dereference)lease_dereference, 0);
-               if (!lease_uid_hash)
+               if (!lease_ip_new_hash(&lease_ip_addr_hash, LEASE_HASH_SIZE,
+                                      MDL))
                        log_fatal ("Can't allocate lease/ip hash");
        }
        if (!lease_hw_addr_hash) {
-               lease_hw_addr_hash =
-                       new_hash ((hash_reference)lease_reference,
-                                 (hash_dereference)lease_dereference, 0);
-               if (!lease_uid_hash)
+               if (!lease_id_new_hash(&lease_hw_addr_hash, LEASE_HASH_SIZE,
+                                      MDL))
                        log_fatal ("Can't allocate lease/hw hash");
        }
 
-       /* Make sure that high and low addresses are in same subnet. */
-       net = subnet_number (low, subnet -> netmask);
-       if (!addr_eq (net, subnet_number (high, subnet -> netmask))) {
-               strcpy (lowbuf, piaddr (low));
-               strcpy (highbuf, piaddr (high));
-               strcpy (netbuf, piaddr (subnet -> netmask));
-               log_fatal ("Address range %s to %s, netmask %s spans %s!",
-                      lowbuf, highbuf, netbuf, "multiple subnets");
+       /* Make sure that high and low addresses are in this subnet. */
+       if (!addr_eq(subnet->net, subnet_number(low, subnet->netmask))) {
+               strcpy(lowbuf, piaddr(low));
+               strcpy(netbuf, piaddr(subnet->net));
+               log_fatal("bad range, address %s not in subnet %s netmask %s",
+                         lowbuf, netbuf, piaddr(subnet->netmask));
        }
 
-       /* Make sure that the addresses are on the correct subnet. */
-       if (!addr_eq (net, subnet -> net)) {
-               strcpy (lowbuf, piaddr (low));
-               strcpy (highbuf, piaddr (high));
-               strcpy (netbuf, piaddr (subnet -> netmask));
-               log_fatal ("Address range %s to %s not on net %s/%s!",
-                      lowbuf, highbuf, piaddr (subnet -> net), netbuf);
+       if (!addr_eq(subnet->net, subnet_number(high, subnet->netmask))) {
+               strcpy(highbuf, piaddr(high));
+               strcpy(netbuf, piaddr(subnet->net));
+               log_fatal("bad range, address %s not in subnet %s netmask %s",
+                         highbuf, netbuf, piaddr(subnet->netmask));
        }
 
        /* Get the high and low host addresses... */
@@ -481,9 +845,30 @@ void new_address_range (low, high, subnet, pool)
                min = host_addr (high, subnet -> netmask);
        }
 
+       /* get the number of addresses we want, and add it to the pool info
+        * this value is only for use when setting up lease chains and will
+        * be overwritten when expire_all_pools is run
+        */
+       num_addrs = max - min + 1;
+#if defined (BINARY_LEASES)
+       pool->lease_count += num_addrs;
+#endif
+
        /* Get a lease structure for each address in the range. */
 #if defined (COMPACT_LEASES)
-       address_range = new_leases (max - min + 1, MDL);
+       s = (num_addrs + 1) * sizeof (struct lease);
+       /* Check unsigned overflow in new_leases().
+          With 304 byte lease structure (x64_86), this happens at
+          range 10.0.0.0 10.215.148.52; */
+       if (((s % sizeof (struct lease)) != 0) ||
+           ((s / sizeof (struct lease)) != (num_addrs + 1))) {
+               strcpy (lowbuf, piaddr (low));
+               strcpy (highbuf, piaddr (high));
+               parse_warn (cfile, "%s-%s is an overly large address range.",
+                          lowbuf, highbuf);
+               log_fatal ("Memory overflow.");
+       }
+       address_range = new_leases (num_addrs, MDL);
        if (!address_range) {
                strcpy (lowbuf, piaddr (low));
                strcpy (highbuf, piaddr (high));
@@ -493,7 +878,7 @@ void new_address_range (low, high, subnet, pool)
 #endif
 
        /* Fill out the lease structures with some minimal information. */
-       for (i = 0; i < max - min + 1; i++) {
+       for (i = 0; i < num_addrs; i++) {
                struct lease *lp = (struct lease *)0;
 #if defined (COMPACT_LEASES)
                omapi_object_initialize ((omapi_object_t *)&address_range [i],
@@ -509,73 +894,51 @@ void new_address_range (low, high, subnet, pool)
                                                    i + min)),
                                   isc_result_totext (status));
 #endif
-               lp -> ip_addr = ip_addr (subnet -> net,
-                                        subnet -> netmask, i + min);
-               lp -> starts = lp -> timestamp = MIN_TIME;
-               lp -> ends = MIN_TIME;
-               subnet_reference (&lp -> subnet, subnet, MDL);
-               pool_reference (&lp -> pool, pool, MDL);
-               lp -> binding_state = FTS_FREE;
-               lp -> next_binding_state = FTS_FREE;
-               lp -> flags = 0;
+               lp->ip_addr = ip_addr(subnet->net, subnet->netmask, i + min);
+               lp->starts = MIN_TIME;
+               lp->ends = MIN_TIME;
+               subnet_reference(&lp->subnet, subnet, MDL);
+               pool_reference(&lp->pool, pool, MDL);
+               lp->binding_state = FTS_FREE;
+               lp->next_binding_state = FTS_FREE;
+               lp->rewind_binding_state = FTS_FREE;
+               lp->flags = 0;
 
                /* Remember the lease in the IP address hash. */
                if (find_lease_by_ip_addr (&lt, lp -> ip_addr, MDL)) {
                        if (lt -> pool) {
-                               log_error ("duplicate entries for lease %s",
-                                          piaddr (lp -> ip_addr));
+                               parse_warn (cfile,
+                                           "lease %s is declared twice!",
+                                           piaddr (lp -> ip_addr));
                        } else
                                pool_reference (&lt -> pool, pool, MDL);
                        lease_dereference (&lt, MDL);
                } else
-                       lease_hash_add (lease_ip_addr_hash,
-                                       lp -> ip_addr.iabuf,
-                                       lp -> ip_addr.len, lp, MDL);
+                       lease_ip_hash_add(lease_ip_addr_hash,
+                                         lp->ip_addr.iabuf, lp->ip_addr.len,
+                                         lp, MDL);
+               /* Put the lease on the chain for the caller. */
+               if (lpchain) {
+                       if (*lpchain) {
+                               lease_reference (&lp -> next, *lpchain, MDL);
+                               lease_dereference (lpchain, MDL);
+                       }
+                       lease_reference (lpchain, lp, MDL);
+               }
                lease_dereference (&lp, MDL);
        }
 }
 
-
-#if defined (COMPACT_LEASES)
-struct lease *free_leases;
-
-/* If we are allocating leases in aggregations, there's really no way
-   to free one, although perhaps we can maintain a free list. */
-
-isc_result_t dhcp_lease_free (omapi_object_t *lo,
-                             const char *file, int line)
-{
-       struct lease *lease;
-       if (lo -> type != dhcp_type_lease)
-               return ISC_R_INVALIDARG;
-       lease = (struct lease *)lo;
-       lease -> next = free_leases;
-       free_leases = lease;
-       return ISC_R_SUCCESS;
-}
-
-isc_result_t dhcp_lease_get (omapi_object_t **lp,
-                            const char *file, int line)
-{
-       struct lease **lease = (struct lease **)lp;
-       struct lease *lt;
-
-       if (free_leases) {
-               lt = free_leases;
-               free_leases = lt -> next;
-               *lease = lt;
-               return ISC_R_SUCCESS;
-       }
-       return ISC_R_NOMEMORY;
-}
-#endif
-
 int find_subnet (struct subnet **sp,
                 struct iaddr addr, const char *file, int line)
 {
        struct subnet *rv;
 
        for (rv = subnets; rv; rv = rv -> next_subnet) {
+#if defined(DHCP4o6)
+               if (addr.len != rv->netmask.len)
+                       continue;
+#endif
                if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
                        if (subnet_reference (sp, rv,
                                              file, line) != ISC_R_SUCCESS)
@@ -593,6 +956,10 @@ int find_grouped_subnet (struct subnet **sp,
        struct subnet *rv;
 
        for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
+#if defined(DHCP4o6)
+               if (addr.len != rv->netmask.len)
+                       continue;
+#endif
                if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
                        if (subnet_reference (sp, rv,
                                              file, line) != ISC_R_SUCCESS)
@@ -603,29 +970,33 @@ int find_grouped_subnet (struct subnet **sp,
        return 0;
 }
 
-int subnet_inner_than (subnet, scan, warnp)
-       struct subnet *subnet, *scan;
-       int warnp;
-{
-       if (addr_eq (subnet_number (subnet -> net, scan -> netmask),
-                    scan -> net) ||
-           addr_eq (subnet_number (scan -> net, subnet -> netmask),
-                    subnet -> net)) {
-               char n1buf [16];
+/* XXX: could speed up if everyone had a prefix length */
+int
+subnet_inner_than(const struct subnet *subnet,
+                 const struct subnet *scan,
+                 int warnp) {
+#if defined(DHCP4o6)
+       if (subnet->net.len != scan->net.len)
+               return 0;
+#endif
+       if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) ||
+           addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) {
+               char n1buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255")];
                int i, j;
-               for (i = 0; i < 32; i++)
-                       if (subnet -> netmask.iabuf [3 - (i >> 3)]
+               for (i = 0; i < 128; i++)
+                       if (subnet->netmask.iabuf[3 - (i >> 3)]
                            & (1 << (i & 7)))
                                break;
-               for (j = 0; j < 32; j++)
-                       if (scan -> netmask.iabuf [3 - (j >> 3)] &
+               for (j = 0; j < 128; j++)
+                       if (scan->netmask.iabuf[3 - (j >> 3)] &
                            (1 << (j & 7)))
                                break;
-               strcpy (n1buf, piaddr (subnet -> net));
-               if (warnp)
-                       log_error ("%ssubnet %s/%d overlaps subnet %s/%d",
-                             "Warning: ", n1buf, 32 - i,
-                             piaddr (scan -> net), 32 - j);
+               if (warnp) {
+                       strcpy(n1buf, piaddr(subnet->net));
+                       log_error("Warning: subnet %s/%d overlaps subnet %s/%d",
+                             n1buf, 32 - i,
+                             piaddr(scan->net), 32 - j);
+               }
                if (i < j)
                        return 1;
        }
@@ -636,30 +1007,48 @@ int subnet_inner_than (subnet, scan, warnp)
 void enter_subnet (subnet)
        struct subnet *subnet;
 {
-       struct subnet *scan, *prev = (struct subnet *)0;
+       struct subnet *scan = (struct subnet *)0;
+       struct subnet *next = (struct subnet *)0;
+       struct subnet *prev = (struct subnet *)0;
 
        /* Check for duplicates... */
-       for (scan = subnets; scan; scan = scan -> next_subnet) {
-               /* When we find a conflict, make sure that the
-                  subnet with the narrowest subnet mask comes
-                  first. */
-               if (subnet_inner_than (subnet, scan, 1)) {
-                       if (prev) {
-                               subnet_reference (&prev -> next_subnet,
-                                                 subnet, MDL);
-                       } else
-                               subnet_reference (&subnets, subnet, MDL);
-                       subnet_reference (&subnet -> next_subnet, scan, MDL);
-                       return;
+       if (subnets)
+           subnet_reference (&next, subnets, MDL);
+       while (next) {
+           subnet_reference (&scan, next, MDL);
+           subnet_dereference (&next, MDL);
+
+           /* When we find a conflict, make sure that the
+              subnet with the narrowest subnet mask comes
+              first. */
+           if (subnet_inner_than (subnet, scan, 1)) {
+               if (prev) {
+                   if (prev -> next_subnet)
+                       subnet_dereference (&prev -> next_subnet, MDL);
+                   subnet_reference (&prev -> next_subnet, subnet, MDL);
+                   subnet_dereference (&prev, MDL);
+               } else {
+                   subnet_dereference (&subnets, MDL);
+                   subnet_reference (&subnets, subnet, MDL);
                }
-               prev = scan;
+               subnet_reference (&subnet -> next_subnet, scan, MDL);
+               subnet_dereference (&scan, MDL);
+               return;
+           }
+           subnet_reference (&prev, scan, MDL);
+           subnet_dereference (&scan, MDL);
        }
+       if (prev)
+               subnet_dereference (&prev, MDL);
 
        /* XXX use the BSD radix tree code instead of a linked list. */
-       subnet -> next_subnet = subnets;
-       subnets = subnet;
+       if (subnets) {
+               subnet_reference (&subnet -> next_subnet, subnets, MDL);
+               subnet_dereference (&subnets, MDL);
+       }
+       subnet_reference (&subnets, subnet, MDL);
 }
-       
+
 /* Enter a new shared network into the shared network list. */
 
 void enter_shared_network (share)
@@ -672,7 +1061,7 @@ void enter_shared_network (share)
        }
        shared_network_reference (&shared_networks, share, MDL);
 }
-       
+
 void new_shared_network_interface (cfile, share, name)
        struct parse *cfile;
        struct shared_network *share;
@@ -682,12 +1071,12 @@ void new_shared_network_interface (cfile, share, name)
        isc_result_t status;
 
        if (share -> interface) {
-               parse_warn (cfile, 
+               parse_warn (cfile,
                            "A subnet or shared network can't be connected %s",
                            "to two interfaces.");
                return;
        }
-       
+
        for (ip = interfaces; ip; ip = ip -> next)
                if (!strcmp (ip -> name, name))
                        break;
@@ -723,7 +1112,6 @@ void enter_lease (lease)
        struct lease *lease;
 {
        struct lease *comp = (struct lease *)0;
-       isc_result_t status;
 
        if (find_lease_by_ip_addr (&comp, lease -> ip_addr, MDL)) {
                if (!comp -> pool) {
@@ -735,9 +1123,9 @@ void enter_lease (lease)
                if (comp -> subnet)
                        subnet_reference (&lease -> subnet,
                                          comp -> subnet, MDL);
-               lease_hash_delete (lease_ip_addr_hash,
-                                  lease -> ip_addr.iabuf,
-                                  lease -> ip_addr.len, MDL);
+               lease_ip_hash_delete(lease_ip_addr_hash,
+                                    lease->ip_addr.iabuf, lease->ip_addr.len,
+                                    MDL);
                lease_dereference (&comp, MDL);
        }
 
@@ -754,9 +1142,8 @@ void enter_lease (lease)
                log_error ("lease %s: no subnet.", piaddr (lease -> ip_addr));
                return;
        }
-       lease_hash_add (lease_ip_addr_hash,
-                       lease -> ip_addr.iabuf,
-                       lease -> ip_addr.len, lease, MDL);
+       lease_ip_hash_add(lease_ip_addr_hash, lease->ip_addr.iabuf,
+                         lease->ip_addr.len, lease, MDL);
 }
 
 /* Replace the data in an existing lease with the data in a new lease;
@@ -764,25 +1151,24 @@ void enter_lease (lease)
    list of leases by expiry time so that we can always find the oldest
    lease. */
 
-int supersede_lease (comp, lease, commit, propogate, pimmediate)
+int supersede_lease (comp, lease, commit, propogate, pimmediate, from_pool)
        struct lease *comp, *lease;
        int commit;
        int propogate;
        int pimmediate;
+       int from_pool;
 {
-       int enter_uid = 0;
-       int enter_hwaddr = 0;
-       struct lease *lp, **lq, *prev;
-       TIME lp_next_state;
-
+       LEASE_STRUCT_PTR lq;
+       struct timeval tv;
 #if defined (FAILOVER_PROTOCOL)
+       int do_pool_check = 0;
+
        /* We must commit leases before sending updates regarding them
           to failover peers.  It is, therefore, an error to set pimmediate
           and not commit. */
        if (pimmediate && !commit)
                return 0;
 #endif
-
        /* If there is no sample lease, just do the move. */
        if (!lease)
                goto just_move_it;
@@ -801,9 +1187,8 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
           requested *after* a DHCP lease has been assigned. */
 
        if (lease -> binding_state != FTS_ABANDONED &&
-           (comp -> binding_state == FTS_ACTIVE ||
-            comp -> binding_state == FTS_RESERVED ||
-            comp -> binding_state == FTS_BOOTP) &&
+           lease -> next_binding_state != FTS_ABANDONED &&
+           comp -> binding_state == FTS_ACTIVE &&
            (((comp -> uid && lease -> uid) &&
              (comp -> uid_len != lease -> uid_len ||
               memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
@@ -815,38 +1200,30 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                       comp -> hardware_addr.hlen))))) {
                log_error ("Lease conflict at %s",
                      piaddr (comp -> ip_addr));
-               return 0;
        }
 
        /* If there's a Unique ID, dissociate it from the hash
           table and free it if necessary. */
-       if (comp -> uid) {
-               uid_hash_delete (comp);
-               enter_uid = 1;
-               if (comp -> uid != &comp -> uid_buf [0]) {
-                       dfree (comp -> uid, MDL);
-                       comp -> uid_max = 0;
-                       comp -> uid_len = 0;
+       if (comp->uid) {
+               uid_hash_delete(comp);
+               if (comp->uid != comp->uid_buf) {
+                       dfree(comp->uid, MDL);
+                       comp->uid_max = 0;
+                       comp->uid_len = 0;
                }
                comp -> uid = (unsigned char *)0;
-       } else
-               enter_uid = 1;
-       
-       if (comp -> hardware_addr.hlen &&
-           ((comp -> hardware_addr.hlen !=
-             lease -> hardware_addr.hlen) ||
-            memcmp (comp -> hardware_addr.hbuf,
-                    lease -> hardware_addr.hbuf,
-                    comp -> hardware_addr.hlen))) {
-               hw_hash_delete (comp);
-               enter_hwaddr = 1;
-       } else if (!comp -> hardware_addr.hlen)
-               enter_hwaddr = 1;
-       
+       }
+
+       /* If there's a hardware address, remove the lease from its
+        * old position in the hash bucket's ordered list.
+        */
+       if (comp->hardware_addr.hlen)
+               hw_hash_delete(comp);
+
        /* If the lease has been billed to a class, remove the billing. */
        if (comp -> billing_class != lease -> billing_class) {
-               if (comp -> billing_class)
-                       unbill_class (comp, comp -> billing_class);
+               if (comp->billing_class)
+                       unbill_class(comp);
                if (lease -> billing_class)
                        bill_class (comp, lease -> billing_class);
        }
@@ -878,8 +1255,6 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                host_dereference (&comp -> host, MDL);
        host_reference (&comp -> host, lease -> host, MDL);
        comp -> hardware_addr = lease -> hardware_addr;
-       comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) |
-                        (comp -> flags & ~EPHEMERAL_FLAGS));
        if (comp -> scope)
                binding_scope_dereference (&comp -> scope, MDL);
        if (lease -> scope) {
@@ -893,9 +1268,7 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                /* Only retain the agent options if the lease is still
                   affirmatively associated with a client. */
                if (lease -> next_binding_state == FTS_ACTIVE ||
-                   lease -> next_binding_state == FTS_EXPIRED ||
-                   lease -> next_binding_state == FTS_RESERVED ||
-                   lease -> next_binding_state == FTS_BOOTP)
+                   lease -> next_binding_state == FTS_EXPIRED)
                        option_chain_head_reference (&comp -> agent_options,
                                                     lease -> agent_options,
                                                     MDL);
@@ -908,49 +1281,69 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        comp -> client_hostname = lease -> client_hostname;
        lease -> client_hostname = (char *)0;
 
-       if (lease -> on_expiry) {
-               if (comp -> on_expiry)
-                       executable_statement_dereference (&comp -> on_expiry,
-                                                         MDL);
-               executable_statement_reference (&comp -> on_expiry,
-                                               lease -> on_expiry,
+       if (lease->on_star.on_expiry) {
+               if (comp->on_star.on_expiry)
+                       executable_statement_dereference
+                               (&comp->on_star.on_expiry, MDL);
+               executable_statement_reference (&comp->on_star.on_expiry,
+                                               lease->on_star.on_expiry,
                                                MDL);
        }
-       if (lease -> on_commit) {
-               if (comp -> on_commit)
-                       executable_statement_dereference (&comp -> on_commit,
-                                                         MDL);
-               executable_statement_reference (&comp -> on_commit,
-                                               lease -> on_commit,
+       if (lease->on_star.on_commit) {
+               if (comp->on_star.on_commit)
+                       executable_statement_dereference
+                               (&comp->on_star.on_commit, MDL);
+               executable_statement_reference (&comp->on_star.on_commit,
+                                               lease->on_star.on_commit,
                                                MDL);
        }
-       if (lease -> on_release) {
-               if (comp -> on_release)
-                       executable_statement_dereference (&comp -> on_release,
-                                                         MDL);
-               executable_statement_reference (&comp -> on_release,
-                                               lease -> on_release, MDL);
+       if (lease->on_star.on_release) {
+               if (comp->on_star.on_release)
+                       executable_statement_dereference
+                               (&comp->on_star.on_release, MDL);
+               executable_statement_reference (&comp->on_star.on_release,
+                                               lease->on_star.on_release,
+                                               MDL);
        }
-       
+
        /* Record the lease in the uid hash if necessary. */
-       if (enter_uid && comp -> uid) {
-               uid_hash_add (comp);
-       }
-       
+       if (comp->uid)
+               uid_hash_add(comp);
+
        /* Record it in the hardware address hash if necessary. */
-       if (enter_hwaddr && lease -> hardware_addr.hlen) {
-               hw_hash_add (comp);
-       }
-       
+       if (comp->hardware_addr.hlen)
+               hw_hash_add(comp);
+
+       comp->cltt = lease->cltt;
 #if defined (FAILOVER_PROTOCOL)
-       comp -> cltt = lease -> cltt;
-       comp -> tstp = lease -> tstp;
-       comp -> tsfp = lease -> tsfp;
+       comp->tstp = lease->tstp;
+       comp->tsfp = lease->tsfp;
+       comp->atsfp = lease->atsfp;
 #endif /* FAILOVER_PROTOCOL */
-       comp -> ends = lease -> ends;
-       comp -> next_binding_state = lease -> next_binding_state;
+       comp->ends = lease->ends;
+       comp->next_binding_state = lease->next_binding_state;
+
+       /*
+        * If we have a control block pointer copy it in.
+        * We don't zero out an older ponter as it is still
+        * in use.  We shouldn't need to overwrite an
+        * old pointer with a new one as the old transaction
+        * should have been cancelled before getting here.
+        */
+       if (lease->ddns_cb != NULL)
+               comp->ddns_cb = lease->ddns_cb;
 
       just_move_it:
+#if defined (FAILOVER_PROTOCOL)
+       /*
+        * Atsfp should be cleared upon any state change that implies
+        * propagation whether supersede_lease was given a copy lease
+        * structure or not (often from the pool_timer()).
+        */
+       if (propogate)
+               comp->atsfp = 0;
+#endif /* FAILOVER_PROTOCOL */
+
        if (!comp -> pool) {
                log_error ("Supersede_lease: lease %s with no pool.",
                           piaddr (comp -> ip_addr));
@@ -960,13 +1353,19 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        /* Figure out which queue it's on. */
        switch (comp -> binding_state) {
              case FTS_FREE:
-               lq = &comp -> pool -> free;
-               comp -> pool -> free_leases--;
+               if (comp->flags & RESERVED_LEASE)
+                       lq = &comp->pool->reserved;
+               else {
+                       lq = &comp->pool->free;
+                       comp->pool->free_leases--;
+               }
+
+#if defined(FAILOVER_PROTOCOL)
+               do_pool_check = 1;
+#endif
                break;
 
              case FTS_ACTIVE:
-             case FTS_RESERVED:
-             case FTS_BOOTP:
                lq = &comp -> pool -> active;
                break;
 
@@ -981,8 +1380,16 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                break;
 
              case FTS_BACKUP:
-               lq = &comp -> pool -> backup;
-               comp -> pool -> backup_leases--;
+               if (comp->flags & RESERVED_LEASE)
+                       lq = &comp->pool->reserved;
+               else {
+                       lq = &comp->pool->backup;
+                       comp->pool->backup_leases--;
+               }
+
+#if defined(FAILOVER_PROTOCOL)
+               do_pool_check = 1;
+#endif
                break;
 
              default:
@@ -996,34 +1403,14 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
 
        /* Remove the lease from its current place in its current
           timer sequence. */
-       prev = (struct lease *)0;
-       for (lp = *lq; lp; lp = lp -> next) {
-               if (lp == comp)
-                       break;
-               prev = lp;
-       }
+       LEASE_REMOVEP(lq, comp);
 
-       if (!lp) {
-               log_error ("Lease with binding state %s not on its queue.",
-                          (comp -> binding_state < 1 &&
-                           comp -> binding_state < FTS_BOOTP)
-                          ? "unknown"
-                          : binding_state_names [comp -> binding_state - 1]);
-               return 0;
-       }
-       
-       if (prev) {
-               lease_dereference (&prev -> next, MDL);
-               if (comp -> next) {
-                       lease_reference (&prev -> next, comp -> next, MDL);
-                       lease_dereference (&comp -> next, MDL);
-               }
-       } else {
-               lease_dereference (lq, MDL);
-               if (comp -> next) {
-                       lease_reference (lq, comp -> next, MDL);
-                       lease_dereference (&comp -> next, MDL);
-               }
+       /* Now that we've done the flag-affected queue removal
+        * we can update the new lease's flags, if there's an
+        * existing lease */
+       if (lease) {
+               comp->flags = ((lease->flags & ~PERSISTENT_FLAGS) |
+                               (comp->flags & ~EPHEMERAL_FLAGS));
        }
 
        /* Make the state transition. */
@@ -1038,7 +1425,7 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        /* If this is the next lease that will timeout on the pool,
           zap the old timeout and set the timeout on this pool to the
           time that the lease's next event will happen.
-                  
+
           We do not actually set the timeout unless commit is true -
           we don't want to thrash the timer queue when reading the
           lease database.  Instead, the database code calls the
@@ -1046,41 +1433,79 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
           and the expiry code sets the timer if there's anything left
           to expire after it's run any outstanding expiry events on
           the pool. */
-       if (commit &&
+       if ((commit || !pimmediate) &&
            comp -> sort_time != MIN_TIME &&
            comp -> sort_time > cur_time &&
            (comp -> sort_time < comp -> pool -> next_event_time ||
             comp -> pool -> next_event_time == MIN_TIME)) {
                comp -> pool -> next_event_time = comp -> sort_time;
-               add_timeout (comp -> pool -> next_event_time,
+               tv . tv_sec = comp -> pool -> next_event_time;
+               tv . tv_usec = 0;
+               add_timeout (&tv,
                             pool_timer, comp -> pool,
                             (tvref_t)pool_reference,
                             (tvunref_t)pool_dereference);
        }
 
        if (commit) {
+#if defined(FAILOVER_PROTOCOL)
+               /*
+                * If commit and propogate are set, then we can save a
+                * possible fsync later in BNDUPD socket transmission by
+                * stepping the rewind state forward to the new state, in
+                * case it has changed.  This is only worth doing if the
+                * failover connection is currently connected, as in this
+                * case it is likely we will be transmitting to the peer very
+                * shortly.
+                */
+               if (propogate && (comp->pool->failover_peer != NULL) &&
+                   ((comp->pool->failover_peer->service_state ==
+                                                           cooperating) ||
+                    (comp->pool->failover_peer->service_state ==
+                                                           not_responding)))
+                       comp->rewind_binding_state = comp->binding_state;
+#endif
+
                if (!write_lease (comp))
                        return 0;
-               if (!commit_leases ())
-                       return 0;
+               if ((server_starting & SS_NOSYNC) == 0) {
+                       if (!commit_leases ())
+                               return 0;
+               }
        }
 
 #if defined (FAILOVER_PROTOCOL)
        if (propogate) {
+               comp -> desired_binding_state = comp -> binding_state;
                if (!dhcp_failover_queue_update (comp, pimmediate))
                        return 0;
        }
+       if (do_pool_check && comp->pool->failover_peer)
+               dhcp_failover_pool_check(comp->pool);
 #endif
 
+       /* If the current binding state has already expired and we haven't
+        * been called from pool_timer, do an expiry event right now.
+        */
+       /* XXX At some point we should optimize this so that we don't
+          XXX write the lease twice, but this is a safe way to fix the
+          XXX problem for 3.0 (I hope!). */
+       if ((from_pool == 0) &&
+           (commit || !pimmediate) &&
+           (comp->sort_time < cur_time) &&
+           (comp->next_binding_state != comp->binding_state))
+               pool_timer(comp->pool);
+
        return 1;
 }
 
 void make_binding_state_transition (struct lease *lease)
 {
+
 #if defined (FAILOVER_PROTOCOL)
        dhcp_failover_state_t *peer;
 
-       if (lease && lease -> pool && lease -> pool -> failover_peer)
+       if (lease -> pool && lease -> pool -> failover_peer)
                peer = lease -> pool -> failover_peer;
        else
                peer = (dhcp_failover_state_t *)0;
@@ -1092,38 +1517,45 @@ void make_binding_state_transition (struct lease *lease)
            ((
 #if defined (FAILOVER_PROTOCOL)
                    peer &&
-                   lease -> binding_state == FTS_EXPIRED &&
-                   (lease -> next_binding_state == FTS_FREE ||
-                    lease -> next_binding_state == FTS_BACKUP)) ||
+                   (lease->binding_state == FTS_EXPIRED ||
+                    lease->binding_state == FTS_ACTIVE) &&
+                   (lease->next_binding_state == FTS_FREE ||
+                    lease->next_binding_state == FTS_BACKUP)) ||
             (!peer &&
 #endif
-             (lease -> binding_state == FTS_ACTIVE ||
-              lease -> binding_state == FTS_BOOTP ||
-              lease -> binding_state == FTS_RESERVED) &&
+             lease -> binding_state == FTS_ACTIVE &&
              lease -> next_binding_state != FTS_RELEASED))) {
 #if defined (NSUPDATE)
-               ddns_removals (lease);
+               (void) ddns_removals(lease, NULL, NULL, ISC_TRUE);
 #endif
-log_info ("expiry event.");
-               if (lease -> on_expiry) {
-                       execute_statements ((struct binding_value **)0,
-                                           (struct packet *)0, lease,
-                                           (struct client_state *)0,
-                                           (struct option_state *)0,
-                                           (struct option_state *)0, /* XXX */
-                                           &lease -> scope,
-                                           lease -> on_expiry);
-                       if (lease -> on_expiry)
+               if (lease->on_star.on_expiry) {
+                       execute_statements(NULL, NULL, lease,
+                                          NULL, NULL, NULL,
+                                          &lease->scope,
+                                          lease->on_star.on_expiry,
+                                          NULL);
+                       if (lease->on_star.on_expiry)
                                executable_statement_dereference
-                                       (&lease -> on_expiry, MDL);
+                                       (&lease->on_star.on_expiry, MDL);
                }
-               
+
                /* No sense releasing a lease after it's expired. */
-               if (lease -> on_release)
-                       executable_statement_dereference (&lease -> on_release,
-                                                         MDL);
-               if (lease -> billing_class)
-                       unbill_class (lease, lease -> billing_class);
+               if (lease->on_star.on_release)
+                       executable_statement_dereference
+                               (&lease->on_star.on_release, MDL);
+               /* Get rid of client-specific bindings that are only
+                  correct when the lease is active. */
+               if (lease->billing_class)
+                       unbill_class(lease);
+               if (lease -> agent_options)
+                       option_chain_head_dereference (&lease -> agent_options,
+                                                      MDL);
+               if (lease -> client_hostname) {
+                       dfree (lease -> client_hostname, MDL);
+                       lease -> client_hostname = (char *)0;
+               }
+               if (lease -> host)
+                       host_dereference (&lease -> host, MDL);
 
                /* Send the expiry time to the peer. */
                lease -> tstp = lease -> ends;
@@ -1140,33 +1572,54 @@ log_info ("expiry event.");
                     lease -> next_binding_state == FTS_BACKUP)) ||
             (!peer &&
 #endif
-             (lease -> binding_state == FTS_ACTIVE ||
-              lease -> binding_state == FTS_BOOTP ||
-              lease -> binding_state == FTS_RESERVED) &&
+             lease -> binding_state == FTS_ACTIVE &&
              lease -> next_binding_state == FTS_RELEASED))) {
 #if defined (NSUPDATE)
-               ddns_removals (lease);
+               /*
+                * Note: ddns_removals() is also iterated when the lease
+                * enters state 'released' in 'release_lease()'.  The below
+                * is caught when a peer receives a BNDUPD from a failover
+                * peer; it may not have received the client's release (it
+                * may have been offline).
+                *
+                * We could remove the call from release_lease() because
+                * it will also catch here on the originating server after the
+                * peer acknowledges the state change.  However, there could
+                * be many hours inbetween, and in this case we /know/ the
+                * client is no longer using the lease when we receive the
+                * release message.  This is not true of expiry, where the
+                * peer may have extended the lease.
+                */
+               (void) ddns_removals(lease, NULL, NULL, ISC_TRUE);
 #endif
-log_info ("release event.");
-               if (lease -> on_release) {
-                       execute_statements ((struct binding_value **)0,
-                                           (struct packet *)0, lease,
-                                           (struct client_state *)0,
-                                           (struct option_state *)0,
-                                           (struct option_state *)0, /* XXX */
-                                           &lease -> scope,
-                                           lease -> on_release);
-                       executable_statement_dereference (&lease -> on_release,
-                                                         MDL);
+               if (lease->on_star.on_release) {
+                       execute_statements(NULL, NULL, lease,
+                                          NULL, NULL, NULL,
+                                          &lease->scope,
+                                          lease->on_star.on_release,
+                                          NULL);
+                       executable_statement_dereference
+                               (&lease->on_star.on_release, MDL);
                }
-               
-               /* A released lease can't expire. */
-               if (lease -> on_expiry)
-                       executable_statement_dereference (&lease -> on_expiry,
-                                                         MDL);
 
-               if (lease -> billing_class)
-                       unbill_class (lease, lease -> billing_class);
+               /* A released lease can't expire. */
+               if (lease->on_star.on_expiry)
+                       executable_statement_dereference
+                               (&lease->on_star.on_expiry, MDL);
+
+               /* Get rid of client-specific bindings that are only
+                  correct when the lease is active. */
+               if (lease->billing_class)
+                       unbill_class(lease);
+               if (lease -> agent_options)
+                       option_chain_head_dereference (&lease -> agent_options,
+                                                      MDL);
+               if (lease -> client_hostname) {
+                       dfree (lease -> client_hostname, MDL);
+                       lease -> client_hostname = (char *)0;
+               }
+               if (lease -> host)
+                       host_dereference (&lease -> host, MDL);
 
                /* Send the release time (should be == cur_time) to the
                   peer. */
@@ -1183,7 +1636,6 @@ log_info ("release event.");
        lease -> binding_state = lease -> next_binding_state;
        switch (lease -> binding_state) {
              case FTS_ACTIVE:
-             case FTS_BOOTP:
 #if defined (FAILOVER_PROTOCOL)
                if (lease -> pool && lease -> pool -> failover_peer)
                        lease -> next_binding_state = FTS_EXPIRED;
@@ -1196,12 +1648,23 @@ log_info ("release event.");
              case FTS_RELEASED:
              case FTS_ABANDONED:
              case FTS_RESET:
-               lease -> next_binding_state = FTS_FREE;
+               lease->next_binding_state = FTS_FREE;
+#if defined(FAILOVER_PROTOCOL)
+               /* If we are not in partner_down, leases don't go from
+                  EXPIRED to FREE on a timeout - only on an update.
+                  If we're in partner_down, they expire at mclt past
+                  the time we entered partner_down. */
+               if ((lease->pool != NULL) &&
+                   (lease->pool->failover_peer != NULL) &&
+                   (lease->pool->failover_peer->me.state == partner_down))
+                       lease->tsfp =
+                           (lease->pool->failover_peer->me.stos +
+                            lease->pool->failover_peer->mclt);
+#endif /* FAILOVER_PROTOCOL */
                break;
 
              case FTS_FREE:
              case FTS_BACKUP:
-             case FTS_RESERVED:
                lease -> next_binding_state = lease -> binding_state;
                break;
        }
@@ -1210,7 +1673,6 @@ log_info ("release event.");
                   piaddr (lease -> ip_addr),
                   binding_state_print (lease -> next_binding_state));
 #endif
-
 }
 
 /* Copy the contents of one lease into another, correctly maintaining
@@ -1228,7 +1690,6 @@ int lease_copy (struct lease **lp,
        lt -> ip_addr = lease -> ip_addr;
        lt -> starts = lease -> starts;
        lt -> ends = lease -> ends;
-       lt -> timestamp = lease -> timestamp;
        lt -> uid_len = lease -> uid_len;
        lt -> uid_max = lease -> uid_max;
        if (lease -> uid == lease -> uid_buf) {
@@ -1264,26 +1725,28 @@ int lease_copy (struct lease **lp,
        class_reference (&lt -> billing_class,
                         lease -> billing_class, file, line);
        lt -> hardware_addr = lease -> hardware_addr;
-       if (lease -> on_expiry)
-               executable_statement_reference (&lt -> on_expiry,
-                                               lease -> on_expiry,
+       if (lease->on_star.on_expiry)
+               executable_statement_reference (&lt->on_star.on_expiry,
+                                               lease->on_star.on_expiry,
                                                file, line);
-       if (lease -> on_commit)
-               executable_statement_reference (&lt -> on_commit,
-                                               lease -> on_commit,
+       if (lease->on_star.on_commit)
+               executable_statement_reference (&lt->on_star.on_commit,
+                                               lease->on_star.on_commit,
                                                file, line);
-       if (lease -> on_release)
-               executable_statement_reference (&lt -> on_release,
-                                               lease -> on_release,
+       if (lease->on_star.on_release)
+               executable_statement_reference (&lt->on_star.on_release,
+                                               lease->on_star.on_release,
                                                file, line);
-       lt -> flags = lease -> flags;
-       lt -> tstp = lease -> tstp;
-       lt -> tsfp = lease -> tsfp;
-       lt -> cltt = lease -> cltt;
-       lt -> binding_state = lease -> binding_state;
-       lt -> next_binding_state = lease -> next_binding_state;
-       status = lease_reference (lp, lt, file, line);
-       lease_dereference (&lt, MDL);
+       lt->flags = lease->flags;
+       lt->tstp = lease->tstp;
+       lt->tsfp = lease->tsfp;
+       lt->atsfp = lease->atsfp;
+       lt->cltt = lease -> cltt;
+       lt->binding_state = lease->binding_state;
+       lt->next_binding_state = lease->next_binding_state;
+       lt->rewind_binding_state = lease->rewind_binding_state;
+       status = lease_reference(lp, lt, file, line);
+       lease_dereference(&lt, MDL);
        return status == ISC_R_SUCCESS;
 }
 
@@ -1295,46 +1758,68 @@ void release_lease (lease, packet)
        /* If there are statements to execute when the lease is
           released, execute them. */
 #if defined (NSUPDATE)
-       ddns_removals (lease);
+       (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
 #endif
-       if (lease -> on_release) {
-               execute_statements ((struct binding_value **)0,
-                                   packet, lease, (struct client_state *)0,
-                                   packet -> options,
-                                   (struct option_state *)0, /* XXX */
-                                   &lease -> scope, lease -> on_release);
-               if (lease -> on_release)
-                       executable_statement_dereference (&lease -> on_release,
-                                                         MDL);
+       if (lease->on_star.on_release) {
+               execute_statements (NULL, packet, lease,
+                                   NULL, packet->options,
+                                   NULL, &lease->scope,
+                                   lease->on_star.on_release, NULL);
+               if (lease->on_star.on_release)
+                       executable_statement_dereference
+                               (&lease->on_star.on_release, MDL);
        }
 
        /* We do either the on_release or the on_expiry events, but
           not both (it's possible that they could be the same,
           in any case). */
-       if (lease -> on_expiry)
-               executable_statement_dereference (&lease -> on_expiry, MDL);
-
-       if (lease -> ends > cur_time) {
-               if (lease -> on_commit)
-                       executable_statement_dereference (&lease -> on_commit,
-                                                         MDL);
+       if (lease->on_star.on_expiry)
+               executable_statement_dereference
+                       (&lease->on_star.on_expiry, MDL);
+
+       if (lease -> binding_state != FTS_FREE &&
+           lease -> binding_state != FTS_BACKUP &&
+           lease -> binding_state != FTS_RELEASED &&
+           lease -> binding_state != FTS_EXPIRED &&
+           lease -> binding_state != FTS_RESET) {
+               if (lease->on_star.on_commit)
+                       executable_statement_dereference
+                               (&lease->on_star.on_commit, MDL);
 
                /* Blow away any bindings. */
                if (lease -> scope)
                        binding_scope_dereference (&lease -> scope, MDL);
+
+               /* Set sort times to the present. */
                lease -> ends = cur_time;
+               /* Lower layers of muckery set tstp to ->ends.  But we send
+                * protocol messages before this.  So it is best to set
+                * tstp now anyway.
+                */
+               lease->tstp = cur_time;
 #if defined (FAILOVER_PROTOCOL)
                if (lease -> pool && lease -> pool -> failover_peer) {
-                       lease -> next_binding_state = FTS_RELEASED;
+                       dhcp_failover_state_t *peer = NULL;
+
+                       if (lease->pool != NULL)
+                               peer = lease->pool->failover_peer;
+
+                       if ((peer->service_state == not_cooperating) &&
+                           (((peer->i_am == primary) &&
+                             (lease->rewind_binding_state == FTS_FREE)) ||
+                            ((peer->i_am == secondary) &&
+                             (lease->rewind_binding_state == FTS_BACKUP)))) {
+                               lease->next_binding_state =
+                                                 lease->rewind_binding_state;
+                       } else
+                               lease -> next_binding_state = FTS_RELEASED;
                } else {
                        lease -> next_binding_state = FTS_FREE;
                }
 #else
                lease -> next_binding_state = FTS_FREE;
 #endif
-               if (lease -> billing_class)
-                       unbill_class (&lease -> billing_class, MDL);
-               supersede_lease (lease, (struct lease *)0, 1, 1, 1);
+               supersede_lease(lease, NULL, 1, 1, 1, 0);
        }
 }
 
@@ -1345,41 +1830,50 @@ void abandon_lease (lease, message)
        struct lease *lease;
        const char *message;
 {
-       struct lease *lt = (struct lease *)0;
+       struct lease *lt = NULL;
+#if defined (NSUPDATE)
+       (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
+#endif
 
-       if (!lease_copy (&lt, lease, MDL))
+       if (!lease_copy(&lt, lease, MDL)) {
                return;
+       }
 
-#if 0
-       if (lt -> on_expiry)
-               executable_statement_dereference (&lease -> on_expiry, MDL);
-       if (lt -> on_release)
-               executable_statement_dereference (&lease -> on_release, MDL);
-       if (lt -> on_commit)
-               executable_statement_dereference (&lease -> on_commit, MDL);
-
-       /* Blow away any bindings. */
-       if (lt -> scope)
-               binding_scope_dereference (&lt -> scope, MDL);
-#endif
+       if (lt->scope) {
+               binding_scope_dereference(&lt->scope, MDL);
+       }
 
-       lt -> ends = cur_time; /* XXX */
-       lt -> next_binding_state = FTS_ABANDONED;
+       /* Calculate the abandone expiry time.  If it wraps,
+        * use the maximum expiry time. */
+       lt->ends = cur_time + abandon_lease_time;
+       if (lt->ends < cur_time || lt->ends > MAX_TIME) {
+               lt->ends = MAX_TIME;
+       }
 
-       log_error ("Abandoning IP address %s: %s",
-             piaddr (lease -> ip_addr), message);
-       lt -> hardware_addr.hlen = 0;
-       if (lt -> uid != lt -> uid_buf)
-               dfree (lt -> uid, MDL);
-       lt -> uid = (unsigned char *)0;
-       lt -> uid_len = 0;
-       lt -> uid_max = 0;
-       if (lt -> billing_class)
-               unbill_class (&lt -> billing_class, MDL);
-       supersede_lease (lease, lt, 1, 1, 1);
-       lease_dereference (&lt, MDL);
+       lt->next_binding_state = FTS_ABANDONED;
+
+       log_error ("Abandoning IP address %s: %s", piaddr(lease->ip_addr),
+                    message);
+       lt->hardware_addr.hlen = 0;
+       if (lt->uid && lt->uid != lt->uid_buf) {
+               dfree(lt->uid, MDL);
+       }
+
+       lt->uid = NULL;
+       lt->uid_len = 0;
+       lt->uid_max = 0;
+       supersede_lease(lease, lt, 1, 1, 1, 0);
+       lease_dereference(&lt, MDL);
 }
 
+#if 0
+/*
+ * This doesn't appear to be in use for anything anymore.
+ * I'm ifdeffing it now and if there are no complaints in
+ * the future it will be removed.
+ * SAR
+ */
+
 /* Abandon the specified lease (set its timeout to infinity and its
    particulars to zero, and re-hash it as appropriate. */
 
@@ -1387,23 +1881,13 @@ void dissociate_lease (lease)
        struct lease *lease;
 {
        struct lease *lt = (struct lease *)0;
+#if defined (NSUPDATE)
+       (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
+#endif
 
        if (!lease_copy (&lt, lease, MDL))
                return;
 
-#if 0
-       if (lt -> on_expiry)
-               executable_statement_dereference (&lease -> on_expiry, MDL);
-       if (lt -> on_release)
-               executable_statement_dereference (&lease -> on_release, MDL);
-       if (lt -> on_commit)
-               executable_statement_dereference (&lease -> on_commit, MDL);
-
-       /* Blow away any bindings. */
-       if (lt -> scope)
-               binding_scope_dereference (&lt -> scope, MDL);
-#endif
-
 #if defined (FAILOVER_PROTOCOL)
        if (lease -> pool && lease -> pool -> failover_peer) {
                lt -> next_binding_state = FTS_RESET;
@@ -1415,61 +1899,92 @@ void dissociate_lease (lease)
 #endif
        lt -> ends = cur_time; /* XXX */
        lt -> hardware_addr.hlen = 0;
-       if (lt -> uid != lt -> uid_buf)
+       if (lt -> uid && lt -> uid != lt -> uid_buf)
                dfree (lt -> uid, MDL);
        lt -> uid = (unsigned char *)0;
        lt -> uid_len = 0;
        lt -> uid_max = 0;
-       if (lt -> billing_class)
-               unbill_class (&lt -> billing_class, MDL);
-       supersede_lease (lease, lt, 1, 1, 1);
+       supersede_lease (lease, lt, 1, 1, 1, 0);
        lease_dereference (&lt, MDL);
 }
+#endif
 
 /* Timer called when a lease in a particular pool expires. */
 void pool_timer (vpool)
        void *vpool;
 {
        struct pool *pool;
-       struct lease *lt = (struct lease *)0;
-       struct lease *next = (struct lease *)0;
-       struct lease *lease = (struct lease *)0;
-       struct lease **lptr [5];
-       TIME next_expiry = MAX_TIME;
-       int i;
-
-       pool = (struct pool *)vpool;
-
+       struct lease *next = NULL;
+       struct lease *lease = NULL;
+       struct lease *ltemp = NULL;
 #define FREE_LEASES 0
-       lptr [FREE_LEASES] = &pool -> free;
 #define ACTIVE_LEASES 1
-       lptr [ACTIVE_LEASES] = &pool -> active;
 #define EXPIRED_LEASES 2
-       lptr [EXPIRED_LEASES] = &pool -> expired;
 #define ABANDONED_LEASES 3
-       lptr [ABANDONED_LEASES] = &pool -> abandoned;
 #define BACKUP_LEASES 4
-       lptr [BACKUP_LEASES] = &pool -> backup;
+#define RESERVED_LEASES 5
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
+       TIME next_expiry = MAX_TIME;
+       int i;
+       struct timeval tv;
 
-       for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
+       pool = (struct pool *)vpool;
+
+       lptr[FREE_LEASES] = &pool->free;
+       lptr[ACTIVE_LEASES] = &pool->active;
+       lptr[EXPIRED_LEASES] = &pool->expired;
+       lptr[ABANDONED_LEASES] = &pool->abandoned;
+       lptr[BACKUP_LEASES] = &pool->backup;
+       lptr[RESERVED_LEASES] = &pool->reserved;
+
+       for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
                /* If there's nothing on the queue, skip it. */
-               if (!*(lptr [i]))
+               if (!(LEASE_NOT_EMPTYP(lptr[i])))
                        continue;
-               
-               lease_reference (&lease, *(lptr [i]), MDL);
+
+#if defined (FAILOVER_PROTOCOL)
+               if (pool->failover_peer &&
+                   pool->failover_peer->me.state != partner_down) {
+                       /*
+                        * Normally the secondary doesn't initiate expiration
+                        * events (unless in partner-down), but rather relies
+                        * on the primary to expire the lease.  However, when
+                        * disconnected from its peer, the server is allowed to
+                        * rewind a lease to the previous state that the peer
+                        * would have recorded it.  This means there may be
+                        * opportunities for active->free or active->backup
+                        * expirations while out of contact.
+                        *
+                        * Q: Should we limit this expiration to
+                        *    comms-interrupt rather than not-normal?
+                        */
+                       if ((i == ACTIVE_LEASES) &&
+                           (pool->failover_peer->i_am == secondary) &&
+                           (pool->failover_peer->me.state == normal))
+                               continue;
+
+                       /* Leases in an expired state don't move to
+                          free because of a timeout unless we're in
+                          partner_down. */
+                       if (i == EXPIRED_LEASES)
+                               continue;
+               }
+#endif
+               lease_reference(&lease, LEASE_GET_FIRSTP(lptr[i]), MDL);
 
                while (lease) {
                        /* Remember the next lease in the list. */
                        if (next)
-                               lease_dereference (&next, MDL);
-                       if (lease -> next)
-                               lease_reference (&next, lease -> next, MDL);
+                               lease_dereference(&next, MDL);
+                       ltemp = LEASE_GET_NEXTP(lptr[i], lease);
+                       if (ltemp)
+                               lease_reference(&next, ltemp, MDL);
 
                        /* If we've run out of things to expire on this list,
                           stop. */
-                       if (lease -> sort_time > cur_time) {
-                               if (lease -> sort_time < next_expiry)
-                                       next_expiry = lease -> sort_time;
+                       if (lease->sort_time > cur_time) {
+                               if (lease->sort_time < next_expiry)
+                                       next_expiry = lease->sort_time;
                                break;
                        }
 
@@ -1478,27 +1993,56 @@ void pool_timer (vpool)
                           state change should happen, just call
                           supersede_lease on it to make the change
                           happen. */
-                       if (lease -> next_binding_state !=
-                           lease -> binding_state)
-                               supersede_lease (lease,
-                                                (struct lease *)0, 1, 1, 1);
+                       if (lease->next_binding_state != lease->binding_state)
+                       {
+#if defined(FAILOVER_PROTOCOL)
+                               dhcp_failover_state_t *peer = NULL;
+
+                               if (lease->pool != NULL)
+                                       peer = lease->pool->failover_peer;
+
+                               /* Can we rewind the lease to a free state? */
+                               if (peer != NULL &&
+                                   peer->service_state == not_cooperating &&
+                                   lease->next_binding_state == FTS_EXPIRED &&
+                                   ((peer->i_am == primary &&
+                                     lease->rewind_binding_state == FTS_FREE)
+                                       ||
+                                    (peer->i_am == secondary &&
+                                     lease->rewind_binding_state ==
+                                                               FTS_BACKUP)))
+                                       lease->next_binding_state =
+                                                  lease->rewind_binding_state;
+#endif
+                               supersede_lease(lease, NULL, 1, 1, 1, 1);
+                       }
 
-                       lease_dereference (&lease, MDL);
+                       lease_dereference(&lease, MDL);
                        if (next)
-                               lease_reference (&lease, next, MDL);
+                               lease_reference(&lease, next, MDL);
                }
                if (next)
-                       lease_dereference (&next, MDL);
+                       lease_dereference(&next, MDL);
                if (lease)
-                       lease_dereference (&lease, MDL);
-       }
-       if (next_expiry != MAX_TIME) {
-               pool -> next_event_time = next_expiry;
-               add_timeout (pool -> next_event_time, pool_timer, pool,
+                       lease_dereference(&lease, MDL);
+       }
+
+       /* If we found something to expire and its expiration time
+        * is either less than the current expiration time or the
+        * current expiration time is already expired update the
+        * timer.
+        */
+       if ((next_expiry != MAX_TIME) &&
+           ((pool->next_event_time > next_expiry) ||
+            (pool->next_event_time <= cur_time))) {
+               pool->next_event_time = next_expiry;
+               tv.tv_sec = pool->next_event_time;
+               tv.tv_usec = 0;
+               add_timeout (&tv, pool_timer, pool,
                             (tvref_t)pool_reference,
                             (tvunref_t)pool_dereference);
        } else
-               pool -> next_event_time = MIN_TIME;
+               pool->next_event_time = MIN_TIME;
 
 }
 
@@ -1507,8 +2051,8 @@ void pool_timer (vpool)
 int find_lease_by_ip_addr (struct lease **lp, struct iaddr addr,
                           const char *file, int line)
 {
-       return lease_hash_lookup (lp, lease_ip_addr_hash,
-                                 addr.iabuf, addr.len, file, line);
+       return lease_ip_hash_lookup(lp, lease_ip_addr_hash, addr.iabuf,
+                                   addr.len, file, line);
 }
 
 int find_lease_by_uid (struct lease **lp, const unsigned char *uid,
@@ -1516,7 +2060,7 @@ int find_lease_by_uid (struct lease **lp, const unsigned char *uid,
 {
        if (len == 0)
                return 0;
-       return lease_hash_lookup (lp, lease_uid_hash, uid, len, file, line);
+       return lease_id_hash_lookup (lp, lease_uid_hash, uid, len, file, line);
 }
 
 int find_lease_by_hw_addr (struct lease **lp,
@@ -1524,34 +2068,130 @@ int find_lease_by_hw_addr (struct lease **lp,
                           const char *file, int line)
 {
        if (hwlen == 0)
-               return 0;
-       return lease_hash_lookup (lp, lease_hw_addr_hash,
-                                 hwaddr, hwlen, file, line);
-}
+               return (0);
 
-/* Add the specified lease to the uid hash. */
+       /*
+        * If it's an infiniband address don't bother
+        * as we don't have a useful address to hash.
+        */
+       if ((hwlen == 1) && (hwaddr[0] == HTYPE_INFINIBAND))
+               return (0);
 
-void uid_hash_add (lease)
-       struct lease *lease;
+       return (lease_id_hash_lookup(lp, lease_hw_addr_hash, hwaddr, hwlen,
+                                    file, line));
+}
+
+/* If the lease is preferred over the candidate, return truth.  The
+ * 'cand' and 'lease' names are retained to read more clearly against
+ * the 'uid_hash_add' and 'hw_hash_add' functions (this is common logic
+ * to those two functions).
+ *
+ * 1) ACTIVE leases are preferred.  The active lease with
+ *    the longest lifetime is preferred over shortest.
+ * 2) "transitional states" are next, this time with the
+ *    most recent CLTT.
+ * 3) free/backup/etc states are next, again with CLTT.  In truth we
+ *    should never see reset leases for this.
+ * 4) Abandoned leases are always dead last.
+ */
+static isc_boolean_t
+client_lease_preferred(struct lease *cand, struct lease *lease)
 {
-       struct lease *head = (struct lease *)0;
-       struct lease *next = (struct lease *)0;
+       if (cand->binding_state == FTS_ACTIVE) {
+               if (lease->binding_state == FTS_ACTIVE &&
+                   lease->ends >= cand->ends)
+                       return ISC_TRUE;
+       } else if (cand->binding_state == FTS_EXPIRED ||
+                  cand->binding_state == FTS_RELEASED) {
+               if (lease->binding_state == FTS_ACTIVE)
+                       return ISC_TRUE;
+
+               if ((lease->binding_state == FTS_EXPIRED ||
+                    lease->binding_state == FTS_RELEASED) &&
+                   lease->cltt >= cand->cltt)
+                       return ISC_TRUE;
+       } else if (cand->binding_state != FTS_ABANDONED) {
+               if (lease->binding_state == FTS_ACTIVE ||
+                   lease->binding_state == FTS_EXPIRED ||
+                   lease->binding_state == FTS_RELEASED)
+                       return ISC_TRUE;
+
+               if (lease->binding_state != FTS_ABANDONED &&
+                   lease->cltt >= cand->cltt)
+                       return ISC_TRUE;
+       } else /* (cand->binding_state == FTS_ABANDONED) */ {
+               if (lease->binding_state != FTS_ABANDONED ||
+                   lease->cltt >= cand->cltt)
+                       return ISC_TRUE;
+       }
+
+       return ISC_FALSE;
+}
 
+/* Add the specified lease to the uid hash. */
+void
+uid_hash_add(struct lease *lease)
+{
+       struct lease *head = NULL;
+       struct lease *cand = NULL;
+       struct lease *prev = NULL;
+       struct lease *next = NULL;
 
        /* If it's not in the hash, just add it. */
        if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL))
-               lease_hash_add (lease_uid_hash, lease -> uid,
-                               lease -> uid_len, lease, MDL);
+               lease_id_hash_add(lease_uid_hash, lease->uid, lease->uid_len,
+                                 lease, MDL);
        else {
-               /* Otherwise, attach it to the end of the list. */
-               while (head -> n_uid) {
-                       lease_reference (&next, head -> n_uid, MDL);
-                       lease_dereference (&head, MDL);
-                       lease_reference (&head, next, MDL);
-                       lease_dereference (&next, MDL);
+               /* Otherwise, insert it into the list in order of its
+                * preference for "resuming allocation to the client."
+                *
+                * Because we don't have control of the hash bucket index
+                * directly, we have to remove and re-insert the client
+                * id into the hash if we're inserting onto the head.
+                */
+               lease_reference(&cand, head, MDL);
+               while (cand != NULL) {
+                       if (client_lease_preferred(cand, lease))
+                               break;
+
+                       if (prev != NULL)
+                               lease_dereference(&prev, MDL);
+                       lease_reference(&prev, cand, MDL);
+
+                       if (cand->n_uid != NULL)
+                               lease_reference(&next, cand->n_uid, MDL);
+
+                       lease_dereference(&cand, MDL);
+
+                       if (next != NULL) {
+                               lease_reference(&cand, next, MDL);
+                               lease_dereference(&next, MDL);
+                       }
                }
-               lease_reference (&head -> n_uid, lease, MDL);
-               lease_dereference (&head, MDL);
+
+               /* If we want to insert 'before cand', and prev is NULL,
+                * then it was the head of the list.  Assume that position.
+                */
+               if (prev == NULL) {
+                       lease_reference(&lease->n_uid, head, MDL);
+                       lease_id_hash_delete(lease_uid_hash, lease->uid,
+                                            lease->uid_len, MDL);
+                       lease_id_hash_add(lease_uid_hash, lease->uid,
+                                         lease->uid_len, lease, MDL);
+               } else /* (prev != NULL) */ {
+                       if(prev->n_uid != NULL) {
+                               lease_reference(&lease->n_uid, prev->n_uid,
+                                               MDL);
+                               lease_dereference(&prev->n_uid, MDL);
+                       }
+                       lease_reference(&prev->n_uid, lease, MDL);
+
+                       lease_dereference(&prev, MDL);
+               }
+
+               if (cand != NULL)
+                       lease_dereference(&cand, MDL);
+               lease_dereference(&head, MDL);
        }
 }
 
@@ -1574,13 +2214,12 @@ void uid_hash_delete (lease)
           remove the hash table entry and add a new one with the
           next lease on the list (if there is one). */
        if (head == lease) {
-               lease_hash_delete (lease_uid_hash,
-                                  lease -> uid, lease -> uid_len, MDL);
+               lease_id_hash_delete(lease_uid_hash, lease->uid,
+                                    lease->uid_len, MDL);
                if (lease -> n_uid) {
-                       lease_hash_add (lease_uid_hash,
-                                       lease -> n_uid -> uid,
-                                       lease -> n_uid -> uid_len,
-                                       lease -> n_uid, MDL);
+                       lease_id_hash_add(lease_uid_hash, lease->n_uid->uid,
+                                         lease->n_uid->uid_len, lease->n_uid,
+                                         MDL);
                        lease_dereference (&lease -> n_uid, MDL);
                }
        } else {
@@ -1604,31 +2243,85 @@ void uid_hash_delete (lease)
 }
 
 /* Add the specified lease to the hardware address hash. */
+/* We don't add leases with infiniband addresses to the
+ * hash as there isn't any address to hash on. */
 
-void hw_hash_add (lease)
-       struct lease *lease;
+void
+hw_hash_add(struct lease *lease)
 {
-       struct lease *head = (struct lease *)0;
-       struct lease *next = (struct lease *)0;
+       struct lease *head = NULL;
+       struct lease *cand = NULL;
+       struct lease *prev = NULL;
+       struct lease *next = NULL;
+
+       /*
+        * If it's an infiniband address don't bother
+        * as we don't have a useful address to hash.
+        */
+       if ((lease->hardware_addr.hlen == 1) &&
+           (lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND))
+               return;
 
        /* If it's not in the hash, just add it. */
        if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
                                    lease -> hardware_addr.hlen, MDL))
-               lease_hash_add (lease_hw_addr_hash,
-                               lease -> hardware_addr.hbuf,
-                               lease -> hardware_addr.hlen,
-                               lease, MDL);
+               lease_id_hash_add(lease_hw_addr_hash,
+                                 lease->hardware_addr.hbuf,
+                                 lease->hardware_addr.hlen, lease, MDL);
        else {
-               /* Otherwise, attach it to the end of the list. */
-               while (head -> n_hw) {
-                       lease_reference (&next, head -> n_hw, MDL);
-                       lease_dereference (&head, MDL);
-                       lease_reference (&head, next, MDL);
-                       lease_dereference (&next, MDL);
+               /* Otherwise, insert it into the list in order of its
+                * preference for "resuming allocation to the client."
+                *
+                * Because we don't have control of the hash bucket index
+                * directly, we have to remove and re-insert the client
+                * id into the hash if we're inserting onto the head.
+                */
+               lease_reference(&cand, head, MDL);
+               while (cand != NULL) {
+                       if (client_lease_preferred(cand, lease))
+                               break;
+
+                       if (prev != NULL)
+                               lease_dereference(&prev, MDL);
+                       lease_reference(&prev, cand, MDL);
+
+                       if (cand->n_hw != NULL)
+                               lease_reference(&next, cand->n_hw, MDL);
+
+                       lease_dereference(&cand, MDL);
+
+                       if (next != NULL) {
+                               lease_reference(&cand, next, MDL);
+                               lease_dereference(&next, MDL);
+                       }
                }
 
-               lease_reference (&head -> n_hw, lease, MDL);
-               lease_dereference (&head, MDL);
+               /* If we want to insert 'before cand', and prev is NULL,
+                * then it was the head of the list.  Assume that position.
+                */
+               if (prev == NULL) {
+                       lease_reference(&lease->n_hw, head, MDL);
+                       lease_id_hash_delete(lease_hw_addr_hash,
+                                            lease->hardware_addr.hbuf,
+                                            lease->hardware_addr.hlen, MDL);
+                       lease_id_hash_add(lease_hw_addr_hash,
+                                         lease->hardware_addr.hbuf,
+                                         lease->hardware_addr.hlen,
+                                         lease, MDL);
+               } else /* (prev != NULL) */ {
+                       if(prev->n_hw != NULL) {
+                               lease_reference(&lease->n_hw, prev->n_hw,
+                                               MDL);
+                               lease_dereference(&prev->n_hw, MDL);
+                       }
+                       lease_reference(&prev->n_hw, lease, MDL);
+
+                       lease_dereference(&prev, MDL);
+               }
+
+               if (cand != NULL)
+                       lease_dereference(&cand, MDL);
+               lease_dereference(&head, MDL);
        }
 }
 
@@ -1640,6 +2333,14 @@ void hw_hash_delete (lease)
        struct lease *head = (struct lease *)0;
        struct lease *next = (struct lease *)0;
 
+       /*
+        * If it's an infiniband address don't bother
+        * as we don't have a useful address to hash.
+        */
+       if ((lease->hardware_addr.hlen == 1) &&
+           (lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND))
+               return;
+
        /* If it's not in the hash, we have no work to do. */
        if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
                                    lease -> hardware_addr.hlen, MDL)) {
@@ -1652,15 +2353,15 @@ void hw_hash_delete (lease)
           remove the hash table entry and add a new one with the
           next lease on the list (if there is one). */
        if (head == lease) {
-               lease_hash_delete (lease_hw_addr_hash,
-                                  lease -> hardware_addr.hbuf,
-                                  lease -> hardware_addr.hlen, MDL);
-               if (lease -> n_hw) {
-                       lease_hash_add (lease_hw_addr_hash,
-                                       lease -> n_hw -> hardware_addr.hbuf,
-                                       lease -> n_hw -> hardware_addr.hlen,
-                                       lease -> n_hw, MDL);
-                       lease_dereference (&lease -> n_hw, MDL);
+               lease_id_hash_delete(lease_hw_addr_hash,
+                                    lease->hardware_addr.hbuf,
+                                    lease->hardware_addr.hlen, MDL);
+               if (lease->n_hw) {
+                       lease_id_hash_add(lease_hw_addr_hash,
+                                         lease->n_hw->hardware_addr.hbuf,
+                                         lease->n_hw->hardware_addr.hlen,
+                                         lease->n_hw, MDL);
+                       lease_dereference(&lease->n_hw, MDL);
                }
        } else {
                /* Otherwise, look for the lease in the list of leases
@@ -1687,19 +2388,74 @@ void hw_hash_delete (lease)
                lease_dereference (&head, MDL);
 }
 
-/* Write all interesting leases to permanent storage. */
-
-void write_leases ()
-{
+/* Write v4 leases to permanent storage. */
+int write_leases4(void) {
        struct lease *l;
        struct shared_network *s;
        struct pool *p;
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
+       int num_written = 0, i;
+
+       /* Write all the leases. */
+       for (s = shared_networks; s; s = s->next) {
+           for (p = s->pools; p; p = p->next) {
+               lptr[FREE_LEASES] = &p->free;
+               lptr[ACTIVE_LEASES] = &p->active;
+               lptr[EXPIRED_LEASES] = &p->expired;
+               lptr[ABANDONED_LEASES] = &p->abandoned;
+               lptr[BACKUP_LEASES] = &p->backup;
+               lptr[RESERVED_LEASES] = &p->reserved;
+
+               for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+                   for (l = LEASE_GET_FIRSTP(lptr[i]);
+                        l != NULL;
+                        l = LEASE_GET_NEXTP(lptr[i], l)) {
+#if !defined (DEBUG_DUMP_ALL_LEASES)
+                       if (l->hardware_addr.hlen != 0 || l->uid_len != 0 ||
+                           l->tsfp != 0 || l->binding_state != FTS_FREE)
+#endif
+                       {
+                           if (write_lease(l) == 0)
+                                   return (0);
+                           num_written++;
+                       }
+                   }
+               }
+           }
+       }
+
+       log_info ("Wrote %d leases to leases file.", num_written);
+       return (1);
+}
+
+/* Write all interesting leases to permanent storage. */
+
+int write_leases ()
+{
        struct host_decl *hp;
        struct group_object *gp;
        struct hash_bucket *hb;
+       struct class *cp;
+       struct collection *colp;
        int i;
        int num_written;
-       struct lease **lptr [5];
+
+       /* write all the dynamically-created class declarations. */
+       if (collections->classes) {
+               numclasseswritten = 0;
+               for (colp = collections ; colp ; colp = colp->next) {
+                       for (cp = colp->classes ; cp ; cp = cp->nic) {
+                               write_named_billing_class(
+                                               (unsigned char *)cp->name,
+                                                         0, cp);
+                       }
+               }
+
+               /* XXXJAB this number doesn't include subclasses... */
+               log_info ("Wrote %d class decls to leases file.",
+                         numclasseswritten);
+       }
+
 
        /* Write all the dynamically-created group declarations. */
        if (group_name_hash) {
@@ -1711,7 +2467,8 @@ void write_leases ()
                        if ((gp -> flags & GROUP_OBJECT_DYNAMIC) ||
                            ((gp -> flags & GROUP_OBJECT_STATIC) &&
                             (gp -> flags & GROUP_OBJECT_DELETED))) {
-                               write_group (gp);
+                               if (!write_group (gp))
+                                       return 0;
                                ++num_written;
                        }
                }
@@ -1728,7 +2485,8 @@ void write_leases ()
                        hp = (struct host_decl *)hb -> value;
                        if (((hp -> flags & HOST_DECL_STATIC) &&
                             (hp -> flags & HOST_DECL_DELETED))) {
-                               write_host (hp);
+                               if (!write_host (hp))
+                                       return 0;
                                ++num_written;
                        }
                }
@@ -1745,8 +2503,8 @@ void write_leases ()
                     hb; hb = hb -> next) {
                        hp = (struct host_decl *)hb -> value;
                        if ((hp -> flags & HOST_DECL_DYNAMIC)) {
-                               write_host (hp);
-                               ++num_written;
+                               if (!write_host (hp))
+                                       ++num_written;
                        }
                }
            }
@@ -1756,40 +2514,170 @@ void write_leases ()
 
 #if defined (FAILOVER_PROTOCOL)
        /* Write all the failover states. */
-       dhcp_failover_write_all_states ();
+       if (!dhcp_failover_write_all_states ())
+               return 0;
 #endif
 
-       /* Write all the leases. */
-       num_written = 0;
-       for (s = shared_networks; s; s = s -> next) {
-           for (p = s -> pools; p; p = p -> next) {
-               lptr [FREE_LEASES] = &p -> free;
-               lptr [ACTIVE_LEASES] = &p -> active;
-               lptr [EXPIRED_LEASES] = &p -> expired;
-               lptr [ABANDONED_LEASES] = &p -> abandoned;
-               lptr [BACKUP_LEASES] = &p -> backup;
+       switch (local_family) {
+             case AF_INET:
+               if (write_leases4() == 0)
+                       return (0);
+               break;
+#ifdef DHCPv6
+             case AF_INET6:
+               if (write_leases6() == 0)
+                       return (0);
+               break;
+#endif /* DHCPv6 */
+       }
 
-               for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
-                   for (l = *(lptr [i]); l; l = l -> next) {
-                       if (l -> hardware_addr.hlen ||
-                           l -> uid_len ||
-                           (l -> binding_state != FTS_FREE)) {
-                           if (!write_lease (l))
-                               log_fatal ("Can't rewrite lease database");
-                           num_written++;
-                       }
-                   }
+       if (commit_leases() == 0)
+               return (0);
+       return (1);
+}
+
+#if !defined (BINARY_LEASES)
+/* Unlink all the leases in the queue. */
+void lease_remove_all(struct lease **lq) {
+       struct lease *lp, *ln = NULL;
+
+       /* nothing to do */
+       if (*lq == NULL)
+               return;
+
+       /* We simply derefernce the first item in the list.  When
+        * it's reference counter goes to zero it will be cleaned
+        * and the reference counter
+        *
+        * Get a pointer to the first item in the list and then
+        * drop the reference from the queue pointer
+        */
+       lease_reference(&lp, *lq, MDL);
+       lease_dereference(lq, MDL);
+
+       do {
+               /* if we have a next save a pointer to it and unlink it */
+               if (lp->next) {
+                       lease_reference(&ln, lp->next, MDL);
+                       lease_dereference(&lp->next, MDL);
                }
-           }
+
+               /* get rid of what we currently have */
+               lease_dereference(&lp, MDL);
+
+               /* move the next to the current and loop */
+               lp = ln;
+               ln = NULL;
+       } while (lp != NULL);
+}
+
+/*
+ * This routine walks through a given lease queue (lq) looking
+ * for comp.  If it doesn't find the lease it is a fatal error
+ * as it should be on the given queue.  Once we find the lease
+ * we can remove it from this list.
+ */
+void lease_remove(struct lease **lq, struct lease *comp)
+{
+       struct lease *prev, *lp;
+
+       prev = NULL;
+       for (lp = *lq; lp != NULL; lp = lp->next) {
+               if (lp == comp)
+                       break;
+               prev = lp;
        }
-       log_info ("Wrote %d leases to leases file.", num_written);
-       if (!commit_leases ())
-               log_fatal ("Can't commit leases to new database: %m");
+
+       if (!lp) {
+               log_fatal("Lease with binding state %s not on its queue.",
+                         (comp->binding_state < 1 ||
+                          comp->binding_state > FTS_LAST)
+                         ? "unknown"
+                         : binding_state_names[comp->binding_state - 1]);
+       }
+
+       if (prev) {
+               lease_dereference(&prev->next, MDL);
+               if (comp->next) {
+                       lease_reference(&prev->next, comp->next, MDL);
+                       lease_dereference (&comp->next, MDL);
+               }
+       } else {
+               lease_dereference(lq, MDL);
+               if (comp->next) {
+                       lease_reference(lq, comp->next, MDL);
+                       lease_dereference(&comp->next, MDL);
+               }
+       }
+}
+
+/* This routine inserts comp into lq in a sorted fashion.
+ * The sort key is comp->sort_time, smaller values are
+ * placed earlier in the list.
+ */
+void lease_insert(struct lease **lq, struct lease *comp)
+{
+       struct lease *prev, *lp;
+       static struct lease **last_lq = NULL;
+       static struct lease *last_insert_point = NULL;
+
+       /* This only works during server startup: during runtime, the last
+        * lease may be dequeued in between calls.  If the queue is the same
+        * as was used previously, and the lease structure isn't (this is not
+        * a re-queue), use that as a starting point for the insertion-sort.
+        */
+       if ((server_starting & SS_QFOLLOW) && (lq == last_lq) &&
+           (comp != last_insert_point) &&
+           (last_insert_point->sort_time <= comp->sort_time)) {
+               prev = last_insert_point;
+               lp = prev->next;
+       } else {
+               prev = NULL;
+               lp = *lq;
+       }
+
+       /* Insertion sort the lease onto the appropriate queue. */
+       for (; lp != NULL ; lp = lp->next) {
+               if (lp->sort_time >= comp->sort_time)
+                       break;
+               prev = lp;
+       }
+
+       if (prev) {
+               if (prev->next) {
+                       lease_reference(&comp->next, prev->next, MDL);
+                       lease_dereference(&prev->next, MDL);
+               }
+               lease_reference(&prev->next, comp, MDL);
+       } else {
+               if (*lq) {
+                       lease_reference (&comp->next, *lq, MDL);
+                       lease_dereference(lq, MDL);
+               }
+               lease_reference(lq, comp, MDL);
+       }
+       last_insert_point = comp;
+       last_lq = lq;
+
+       return;
 }
+#endif
 
+/* In addition to placing this lease upon a lease queue depending on its
+ * state, it also keeps track of the number of FREE and BACKUP leases in
+ * existence, and sets the sort_time on the lease.
+ *
+ * Sort_time is used in pool_timer() to determine when the lease will
+ * bubble to the top of the list and be supersede_lease()'d into its next
+ * state (possibly, if all goes well).  Example, ACTIVE leases move to
+ * EXPIRED state when the 'ends' value is reached, so that is its sort
+ * time.  Most queues are sorted by 'ends', since it is generally best
+ * practice to re-use the oldest lease, to reduce address collision
+ * chances.
+ */
 int lease_enqueue (struct lease *comp)
 {
-       struct lease **lq, *prev, *lp;
+       LEASE_STRUCT_PTR lq;
 
        /* No queue to put it on? */
        if (!comp -> pool)
@@ -1798,14 +2686,16 @@ int lease_enqueue (struct lease *comp)
        /* Figure out which queue it's going to. */
        switch (comp -> binding_state) {
              case FTS_FREE:
-               lq = &comp -> pool -> free;
-               comp -> pool -> free_leases++;
+               if (comp->flags & RESERVED_LEASE) {
+                       lq = &comp->pool->reserved;
+               } else {
+                       lq = &comp->pool->free;
+                       comp->pool->free_leases++;
+               }
                comp -> sort_time = comp -> ends;
                break;
 
              case FTS_ACTIVE:
-             case FTS_RESERVED:
-             case FTS_BOOTP:
                lq = &comp -> pool -> active;
                comp -> sort_time = comp -> ends;
                break;
@@ -1814,7 +2704,26 @@ int lease_enqueue (struct lease *comp)
              case FTS_RELEASED:
              case FTS_RESET:
                lq = &comp -> pool -> expired;
-               comp -> sort_time = comp -> ends;
+#if defined(FAILOVER_PROTOCOL)
+               /* In partner_down, tsfp is the time at which the lease
+                * may be reallocated (stos+mclt).  We can do that with
+                * lease_mine_to_reallocate() anywhere between tsfp and
+                * ends.  But we prefer to wait until ends before doing it
+                * automatically (choose the greater of the two).  Note
+                * that 'ends' is usually a historic timestamp in the
+                * case of expired leases, is really only in the future
+                * on released leases, and if we know a lease to be released
+                * the peer might still know it to be active...in which case
+                * it's possible the peer has renewed this lease, so avoid
+                * doing that.
+                */
+               if (comp->pool->failover_peer &&
+                   comp->pool->failover_peer->me.state == partner_down)
+                       comp->sort_time = (comp->tsfp > comp->ends) ?
+                                         comp->tsfp : comp->ends;
+               else
+#endif
+                       comp->sort_time = comp->ends;
 
                break;
 
@@ -1824,8 +2733,12 @@ int lease_enqueue (struct lease *comp)
                break;
 
              case FTS_BACKUP:
-               lq = &comp -> pool -> backup;
-               comp -> pool -> backup_leases++;
+               if (comp->flags & RESERVED_LEASE) {
+                       lq = &comp->pool->reserved;
+               } else {
+                       lq = &comp->pool->backup;
+                       comp->pool->backup_leases++;
+               }
                comp -> sort_time = comp -> ends;
                break;
 
@@ -1838,26 +2751,8 @@ int lease_enqueue (struct lease *comp)
                return 0;
        }
 
-       /* Insertion sort the lease onto the appropriate queue. */
-       prev = (struct lease *)0;
-       for (lp = *lq; lp; lp = lp -> next) {
-               if (lp -> sort_time >= comp -> sort_time)
-                       break;
-               prev = lp;
-       }
-       if (prev) {
-               if (prev -> next) {
-                       lease_reference (&comp -> next, prev -> next, MDL);
-                       lease_dereference (&prev -> next, MDL);
-               }
-               lease_reference (&prev -> next, comp, MDL);
-       } else {
-               if (*lq) {
-                       lease_reference (&comp -> next, *lq, MDL);
-                       lease_dereference (lq, MDL);
-               }
-               lease_reference (lq, comp, MDL);
-       }
+       LEASE_INSERTP(lq, comp);
+
        return 1;
 }
 
@@ -1865,34 +2760,70 @@ int lease_enqueue (struct lease *comp)
    in each appropriate hash, understanding that it's already by definition
    in lease_ip_addr_hash. */
 
-void lease_instantiate (const unsigned char *val, unsigned len,
-                       struct lease *lease)
+isc_result_t
+lease_instantiate(const void *key, unsigned len, void *object)
 {
-
+       struct lease *lease = object;
+       struct class *class;
        /* XXX If the lease doesn't have a pool at this point, it's an
           XXX orphan, which we *should* keep around until it expires,
           XXX but which right now we just forget. */
        if (!lease -> pool) {
-               lease_hash_delete (lease_ip_addr_hash,
-                                  lease -> ip_addr.iabuf,
-                                  lease -> ip_addr.len, MDL);
-               return;
+               lease_ip_hash_delete(lease_ip_addr_hash, lease->ip_addr.iabuf,
+                                    lease->ip_addr.len, MDL);
+               return ISC_R_SUCCESS;
        }
-               
-       /* Put the lease on the right queue. */
-       lease_enqueue (lease);
+
+#if defined (FAILOVER_PROTOCOL)
+       /* If the lease is in FTS_BACKUP but there is no peer, then the
+        * pool must have been formerly configured for failover and
+        * is now configured as standalone. This means we need to
+        * move the lease to FTS_FREE to make it available. */
+       if ((lease->binding_state == FTS_BACKUP) &&
+           (lease->pool->failover_peer == NULL)) {
+#else
+       /* We aren't compiled for failover, so just move to FTS_FREE */
+       if (lease->binding_state == FTS_BACKUP) {
+#endif
+               lease->binding_state = FTS_FREE;
+               lease->next_binding_state = FTS_FREE;
+               lease->rewind_binding_state = FTS_FREE;
+       }
+
+       /* Put the lease on the right queue.  Failure to queue is probably
+        * due to a bogus binding state.  In such a case, we claim success,
+        * so that later leases in a hash_foreach are processed, but we
+        * return early as we really don't want hw address hash entries or
+        * other cruft to surround such a bogus entry.
+        */
+       if (!lease_enqueue(lease))
+               return ISC_R_SUCCESS;
 
        /* Record the lease in the uid hash if possible. */
        if (lease -> uid) {
                uid_hash_add (lease);
        }
-       
+
        /* Record it in the hardware address hash if possible. */
        if (lease -> hardware_addr.hlen) {
                hw_hash_add (lease);
        }
-       
-       return;
+
+       /* If the lease has a billing class, set up the billing. */
+       if (lease -> billing_class) {
+               class = (struct class *)0;
+               class_reference (&class, lease -> billing_class, MDL);
+               class_dereference (&lease -> billing_class, MDL);
+               /* If the lease is available for allocation, the billing
+                  is invalid, so we don't keep it. */
+               if (lease -> binding_state == FTS_ACTIVE ||
+                   lease -> binding_state == FTS_EXPIRED ||
+                   lease -> binding_state == FTS_RELEASED ||
+                   lease -> binding_state == FTS_RESET)
+                       bill_class (lease, class);
+               class_dereference (&class, MDL);
+       }
+       return ISC_R_SUCCESS;
 }
 
 /* Run expiry events on every pool.   This is called on startup so that
@@ -1904,17 +2835,48 @@ void expire_all_pools ()
 {
        struct shared_network *s;
        struct pool *p;
-       struct hash_bucket *hb;
        int i;
        struct lease *l;
-       struct lease **lptr [5];
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
+
+       /* Indicate that we are in the startup phase */
+       server_starting = SS_NOSYNC | SS_QFOLLOW;
+
+#if defined (BINARY_LEASES)
+       /* set up the growth factors for the binary leases.
+        * We use 100% for free, 50% for active and backup
+        * 20% for expired, abandoned and reserved
+        * but no less than 100, 50, and 20.
+        */
+       for (s = shared_networks; s; s = s -> next) {
+           for (p = s -> pools; p != NULL; p = p -> next) {
+               size_t num_f = 100, num_a = 50, num_e = 20;
+               if (p->lease_count > 100) {
+                   num_f = p->lease_count;
+                   num_a = num_f / 2;
+                   num_e = num_f / 5;
+               }
+               lc_init_growth(&p->free, num_f);
+               lc_init_growth(&p->active, num_a);
+               lc_init_growth(&p->expired, num_a);
+               lc_init_growth(&p->abandoned, num_e);
+               lc_init_growth(&p->backup, num_e);
+               lc_init_growth(&p->reserved, num_e);
+           }
+       }
+#endif
 
        /* First, go over the hash list and actually put all the leases
           on the appropriate lists. */
-       lease_hash_foreach (lease_ip_addr_hash, lease_instantiate);
+       lease_ip_hash_foreach(lease_ip_addr_hash, lease_instantiate);
 
        /* Loop through each pool in each shared network and call the
-          expiry routine on the pool. */
+        * expiry routine on the pool.  It is no longer safe to follow
+        * the queue insertion point, as expiration of a lease can move
+        * it between queues (and this may be the lease that function
+        * points at).
+        */
+       server_starting &= ~SS_QFOLLOW;
        for (s = shared_networks; s; s = s -> next) {
            for (p = s -> pools; p; p = p -> next) {
                pool_timer (p);
@@ -1922,32 +2884,49 @@ void expire_all_pools ()
                p -> lease_count = 0;
                p -> free_leases = 0;
                p -> backup_leases = 0;
-               
+
                lptr [FREE_LEASES] = &p -> free;
                lptr [ACTIVE_LEASES] = &p -> active;
                lptr [EXPIRED_LEASES] = &p -> expired;
                lptr [ABANDONED_LEASES] = &p -> abandoned;
                lptr [BACKUP_LEASES] = &p -> backup;
+               lptr [RESERVED_LEASES] = &p->reserved;
 
-               for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
-                   for (l = *(lptr [i]); l; l = l -> next) {
+               for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+                   for (l = LEASE_GET_FIRSTP(lptr[i]);
+                        l != NULL;
+                        l = LEASE_GET_NEXTP(lptr[i], l)) {
                        p -> lease_count++;
                        if (l -> ends <= cur_time) {
-                               if (l -> binding_state == FTS_FREE)
-                                       p -> free_leases++;
-                               else if (l -> binding_state == FTS_BACKUP)
-                                       p -> backup_leases++;
+                               if (l->binding_state == FTS_FREE) {
+                                       if (i == FREE_LEASES)
+                                               p->free_leases++;
+                                       else if (i != RESERVED_LEASES)
+                                               log_fatal("Impossible case "
+                                                         "at %s:%d.", MDL);
+                               } else if (l->binding_state == FTS_BACKUP) {
+                                       if (i == BACKUP_LEASES)
+                                               p->backup_leases++;
+                                       else if (i != RESERVED_LEASES)
+                                               log_fatal("Impossible case "
+                                                         "at %s:%d.", MDL);
+                               }
                        }
 #if defined (FAILOVER_PROTOCOL)
                        if (p -> failover_peer &&
-                           l -> tstp > l -> tsfp &&
-                           !(l -> flags & ON_UPDATE_QUEUE))
+                           l -> tstp > l -> atsfp &&
+                           !(l -> flags & ON_UPDATE_QUEUE)) {
+                               l -> desired_binding_state = l -> binding_state;
                                dhcp_failover_queue_update (l, 1);
+                       }
 #endif
                    }
                }
            }
        }
+
+       /* turn off startup phase */
+       server_starting = 0;
 }
 
 void dump_subnets ()
@@ -1956,7 +2935,7 @@ void dump_subnets ()
        struct shared_network *s;
        struct subnet *n;
        struct pool *p;
-       struct lease **lptr [5];
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
        int i;
 
        log_info ("Subnets:");
@@ -1974,9 +2953,12 @@ void dump_subnets ()
                lptr [EXPIRED_LEASES] = &p -> expired;
                lptr [ABANDONED_LEASES] = &p -> abandoned;
                lptr [BACKUP_LEASES] = &p -> backup;
+               lptr [RESERVED_LEASES] = &p->reserved;
 
-               for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
-                   for (l = *(lptr [i]); l; l = l -> next) {
+               for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+                   for (l = LEASE_GET_FIRSTP(lptr[i]);
+                        l != NULL;
+                        l = LEASE_GET_NEXTP(lptr[i], l)) {
                            print_lease (l);
                    }
                }
@@ -1984,6 +2966,314 @@ void dump_subnets ()
        }
 }
 
-HASH_FUNCTIONS (lease, const unsigned char *, struct lease)
-HASH_FUNCTIONS (host, const unsigned char *, struct host_decl)
-HASH_FUNCTIONS (class, const char *, struct class)
+HASH_FUNCTIONS(lease_ip, const unsigned char *, struct lease, lease_ip_hash_t,
+              lease_reference, lease_dereference, do_ip4_hash)
+HASH_FUNCTIONS(lease_id, const unsigned char *, struct lease, lease_id_hash_t,
+              lease_reference, lease_dereference, do_id_hash)
+HASH_FUNCTIONS (host, const unsigned char *, struct host_decl, host_hash_t,
+               host_reference, host_dereference, do_string_hash)
+HASH_FUNCTIONS (class, const char *, struct class, class_hash_t,
+               class_reference, class_dereference, do_string_hash)
+
+#if defined (DEBUG_MEMORY_LEAKAGE) && \
+               defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+extern struct hash_table *dns_zone_hash;
+extern struct interface_info **interface_vector;
+extern int interface_count;
+dhcp_control_object_t *dhcp_control_object;
+extern struct hash_table *auth_key_hash;
+struct hash_table *universe_hash;
+struct universe **universes;
+int universe_count, universe_max;
+#if 0
+extern int end;
+#endif
+
+#if defined (COMPACT_LEASES)
+extern struct lease *lease_hunks;
+#endif
+
+void free_everything(void)
+{
+       struct subnet *sc = (struct subnet *)0, *sn = (struct subnet *)0;
+       struct shared_network *nc = (struct shared_network *)0,
+               *nn = (struct shared_network *)0;
+       struct pool *pc = (struct pool *)0, *pn = (struct pool *)0;
+       struct lease *lc = NULL, *ln = NULL,  *ltemp = NULL;
+       struct interface_info *ic = (struct interface_info *)0,
+               *in = (struct interface_info *)0;
+       struct class *cc = (struct class *)0, *cn = (struct class *)0;
+       struct collection *lp;
+       int i;
+
+       /* Get rid of all the hash tables. */
+       if (host_hw_addr_hash)
+               host_free_hash_table (&host_hw_addr_hash, MDL);
+       host_hw_addr_hash = 0;
+       if (host_uid_hash)
+               host_free_hash_table (&host_uid_hash, MDL);
+       host_uid_hash = 0;
+       if (lease_uid_hash)
+               lease_id_free_hash_table (&lease_uid_hash, MDL);
+       lease_uid_hash = 0;
+       if (lease_ip_addr_hash)
+               lease_ip_free_hash_table (&lease_ip_addr_hash, MDL);
+       lease_ip_addr_hash = 0;
+       if (lease_hw_addr_hash)
+               lease_id_free_hash_table (&lease_hw_addr_hash, MDL);
+       lease_hw_addr_hash = 0;
+       if (host_name_hash)
+               host_free_hash_table (&host_name_hash, MDL);
+       host_name_hash = 0;
+       if (dns_zone_hash)
+               dns_zone_free_hash_table (&dns_zone_hash, MDL);
+       dns_zone_hash = 0;
+
+       while (host_id_info != NULL) {
+               host_id_info_t *tmp;
+               option_dereference(&host_id_info->option, MDL);
+               host_free_hash_table(&host_id_info->values_hash, MDL);
+               tmp = host_id_info->next;
+               dfree(host_id_info, MDL);
+               host_id_info = tmp;
+       }
+#if 0
+       if (auth_key_hash)
+               auth_key_free_hash_table (&auth_key_hash, MDL);
+#endif
+       auth_key_hash = 0;
+
+       omapi_object_dereference ((omapi_object_t **)&dhcp_control_object,
+                                 MDL);
+
+       for (lp = collections; lp; lp = lp -> next) {
+           if (lp -> classes) {
+               class_reference (&cn, lp -> classes, MDL);
+               do {
+                   if (cn) {
+                       class_reference (&cc, cn, MDL);
+                       class_dereference (&cn, MDL);
+                   }
+                   if (cc -> nic) {
+                       class_reference (&cn, cc -> nic, MDL);
+                       class_dereference (&cc -> nic, MDL);
+                   }
+                   group_dereference (&cc -> group, MDL);
+                   if (cc -> hash) {
+                           class_free_hash_table (&cc -> hash, MDL);
+                           cc -> hash = (struct hash_table *)0;
+                   }
+                   class_dereference (&cc, MDL);
+               } while (cn);
+               class_dereference (&lp -> classes, MDL);
+           }
+       }
+
+       if (interface_vector) {
+           for (i = 0; i < interface_count; i++) {
+               if (interface_vector [i])
+                   interface_dereference (&interface_vector [i], MDL);
+           }
+           dfree (interface_vector, MDL);
+           interface_vector = 0;
+       }
+
+       if (interfaces) {
+           interface_reference (&in, interfaces, MDL);
+           do {
+               if (in) {
+                   interface_reference (&ic, in, MDL);
+                   interface_dereference (&in, MDL);
+               }
+               if (ic -> next) {
+                   interface_reference (&in, ic -> next, MDL);
+                   interface_dereference (&ic -> next, MDL);
+               }
+               omapi_unregister_io_object ((omapi_object_t *)ic);
+               if (ic -> shared_network) {
+                   if (ic -> shared_network -> interface)
+                       interface_dereference
+                               (&ic -> shared_network -> interface, MDL);
+                   shared_network_dereference (&ic -> shared_network, MDL);
+               }
+               interface_dereference (&ic, MDL);
+           } while (in);
+           interface_dereference (&interfaces, MDL);
+       }
+
+       /* Subnets are complicated because of the extra links. */
+       if (subnets) {
+           subnet_reference (&sn, subnets, MDL);
+           do {
+               if (sn) {
+                   subnet_reference (&sc, sn, MDL);
+                   subnet_dereference (&sn, MDL);
+               }
+               if (sc -> next_subnet) {
+                   subnet_reference (&sn, sc -> next_subnet, MDL);
+                   subnet_dereference (&sc -> next_subnet, MDL);
+               }
+               if (sc -> next_sibling)
+                   subnet_dereference (&sc -> next_sibling, MDL);
+               if (sc -> shared_network)
+                   shared_network_dereference (&sc -> shared_network, MDL);
+               group_dereference (&sc -> group, MDL);
+               if (sc -> interface)
+                   interface_dereference (&sc -> interface, MDL);
+               subnet_dereference (&sc, MDL);
+           } while (sn);
+           subnet_dereference (&subnets, MDL);
+       }
+
+       /* So are shared networks. */
+       /* XXX: this doesn't work presently, but i'm ok just filtering
+        * it out of the noise (you get a bigger spike on the real leaks).
+        * It would be good to fix this, but it is not a "real bug," so not
+        * today.  This hack is incomplete, it doesn't trim out sub-values.
+        */
+       if (shared_networks) {
+               shared_network_dereference (&shared_networks, MDL);
+       /* This is the old method (tries to free memory twice, broken) */
+       } else if (0) {
+           shared_network_reference (&nn, shared_networks, MDL);
+           do {
+               if (nn) {
+                   shared_network_reference (&nc, nn, MDL);
+                   shared_network_dereference (&nn, MDL);
+               }
+               if (nc -> next) {
+                   shared_network_reference (&nn, nc -> next, MDL);
+                   shared_network_dereference (&nc -> next, MDL);
+               }
+
+               /* As are pools. */
+               if (nc -> pools) {
+                   pool_reference (&pn, nc -> pools, MDL);
+                   do {
+                       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
+
+                       if (pn) {
+                           pool_reference (&pc, pn, MDL);
+                           pool_dereference (&pn, MDL);
+                       }
+                       if (pc -> next) {
+                           pool_reference (&pn, pc -> next, MDL);
+                           pool_dereference (&pc -> next, MDL);
+                       }
+
+                       lptr [FREE_LEASES] = &pc -> free;
+                       lptr [ACTIVE_LEASES] = &pc -> active;
+                       lptr [EXPIRED_LEASES] = &pc -> expired;
+                       lptr [ABANDONED_LEASES] = &pc -> abandoned;
+                       lptr [BACKUP_LEASES] = &pc -> backup;
+                       lptr [RESERVED_LEASES] = &pc->reserved;
+
+                       /* As (sigh) are leases. */
+                       for (i = FREE_LEASES ; i <= RESERVED_LEASES ; i++) {
+                           if (LEASE_NOT_EMPTYP(lptr[i])) {
+                               lease_reference(&ln, LEASE_GET_FIRSTP(lptr[i]), MDL);
+                               do {
+                                   /* save a pointer to the current lease */
+                                   lease_reference (&lc, ln, MDL);
+                                   lease_dereference (&ln, MDL);
+
+                                   /* get the next lease if there is one */
+                                   ltemp = LEASE_GET_NEXTP(lptr[i], lc);
+                                   if (ltemp != NULL) {
+                                       lease_reference(&ln, ltemp, MDL);
+                                   }
+
+                                   /* remove the current lease from the queue */
+                                   LEASE_REMOVEP(lptr[i], lc);
+
+                                   if (lc -> billing_class)
+                                      class_dereference (&lc -> billing_class,
+                                                         MDL);
+                                   if (lc -> state)
+                                       free_lease_state (lc -> state, MDL);
+                                   lc -> state = (struct lease_state *)0;
+                                   if (lc -> n_hw)
+                                       lease_dereference (&lc -> n_hw, MDL);
+                                   if (lc -> n_uid)
+                                       lease_dereference (&lc -> n_uid, MDL);
+                                   lease_dereference (&lc, MDL);
+                               } while (ln);
+                           }
+                       }
+                       if (pc -> group)
+                           group_dereference (&pc -> group, MDL);
+                       if (pc -> shared_network)
+                           shared_network_dereference (&pc -> shared_network,
+                                                       MDL);
+                       pool_dereference (&pc, MDL);
+                   } while (pn);
+                   pool_dereference (&nc -> pools, MDL);
+               }
+               /* Because of a circular reference, we need to nuke this
+                  manually. */
+               group_dereference (&nc -> group, MDL);
+               shared_network_dereference (&nc, MDL);
+           } while (nn);
+           shared_network_dereference (&shared_networks, MDL);
+       }
+
+       cancel_all_timeouts ();
+       relinquish_timeouts ();
+#if defined(DELAYED_ACK)
+       relinquish_ackqueue();
+#endif
+       trace_free_all ();
+       group_dereference (&root_group, MDL);
+       executable_statement_dereference (&default_classification_rules, MDL);
+
+       shutdown_state = shutdown_drop_omapi_connections;
+       omapi_io_state_foreach (dhcp_io_shutdown, 0);
+       shutdown_state = shutdown_listeners;
+       omapi_io_state_foreach (dhcp_io_shutdown, 0);
+       shutdown_state = shutdown_dhcp;
+       omapi_io_state_foreach (dhcp_io_shutdown, 0);
+
+       omapi_object_dereference ((omapi_object_t **)&icmp_state, MDL);
+
+       universe_free_hash_table (&universe_hash, MDL);
+       for (i = 0; i < universe_count; i++) {
+#if 0
+               union {
+                       const char *c;
+                       char *s;
+               } foo;
+#endif
+               if (universes [i]) {
+                       if (universes[i]->name_hash)
+                           option_name_free_hash_table(
+                                               &universes[i]->name_hash,
+                                               MDL);
+                       if (universes[i]->code_hash)
+                           option_code_free_hash_table(
+                                               &universes[i]->code_hash,
+                                               MDL);
+#if 0
+                       if (universes [i] -> name > (char *)&end) {
+                               foo.c = universes [i] -> name;
+                               dfree (foo.s, MDL);
+                       }
+                       if (universes [i] > (struct universe *)&end)
+                               dfree (universes [i], MDL);
+#endif
+               }
+       }
+       dfree (universes, MDL);
+
+       relinquish_free_lease_states ();
+       relinquish_free_pairs ();
+       relinquish_free_expressions ();
+       relinquish_free_binding_values ();
+       relinquish_free_option_caches ();
+       relinquish_free_packets ();
+#if defined(COMPACT_LEASES)
+       relinquish_lease_hunks ();
+#endif
+       relinquish_hash_bucket_hunks ();
+       omapi_type_relinquish ();
+}
+#endif /* DEBUG_MEMORY_LEAKAGE_ON_EXIT */