--- /dev/null
+/* mdb.c
+
+ Server-specific in-memory database support. */
+
+/*
+ * Copyright (c) 1996-1999 Internet Software Consortium.
+ * Use is subject to license terms which appear in the file named
+ * ISC-LICENSE that should have accompanied this file when you
+ * received it. If a file named ISC-LICENSE did not accompany this
+ * file, or you are not sure the one you have is correct, you may
+ * obtain an applicable copy of the license at:
+ *
+ * http://www.isc.org/isc-license-1.0.html.
+ *
+ * This file is part of the ISC DHCP distribution. The documentation
+ * associated with this file is listed in the file DOCUMENTATION,
+ * included in the top-level directory of this release.
+ *
+ * Support and other services are available for ISC products - see
+ * http://www.isc.org for more information.
+ */
+
+#ifndef lint
+static char copyright[] =
+"$Id: mdb.c,v 1.1 1999/09/28 22:49:31 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.h"
+
+static struct subnet *subnets;
+static 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;
+static struct lease *dangling_leases;
+static struct host_decl *dynamic_hosts;
+
+struct group_object *groups;
+struct hash_table *group_name_hash;
+
+omapi_object_type_t *dhcp_type_host;
+
+isc_result_t enter_host (hd, dynamicp, commit)
+ struct host_decl *hd;
+ int dynamicp;
+ int commit;
+{
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *np = (struct host_decl *)0;
+ struct executable_statement *esp;
+
+ if (!host_name_hash) {
+ host_name_hash = new_hash ();
+ if (!host_name_hash)
+ log_fatal ("Can't allocate host name hash");
+ } else {
+ hp = (struct host_decl *)
+ hash_lookup (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name));
+
+ /* If there isn't already a host decl matching this
+ address, add it to the hash table. */
+ if (!hp) {
+ add_hash (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name),
+ (unsigned char *)hd);
+ hd -> refcnt++; /* XXX */
+ } else
+ /* XXX actually, we have to delete the old one
+ XXX carefully and replace it. Not done yet. */
+ return ISC_R_EXISTS;
+ }
+
+ if (dynamicp) {
+ hd -> flags |= HOST_DECL_DYNAMIC;
+ hd -> n_dynamic = dynamic_hosts;
+ dynamic_hosts = hd;
+ }
+ hd -> n_ipaddr = (struct host_decl *)0;
+
+ if (!hd -> type)
+ hd -> type = dhcp_type_host;
+
+ if (hd -> interface.hlen) {
+ if (!host_hw_addr_hash) {
+ host_hw_addr_hash = new_hash ();
+ if (!host_hw_addr_hash)
+ log_fatal ("Can't allocate host/hw hash");
+ } else
+ hp = (struct host_decl *)
+ hash_lookup (host_hw_addr_hash,
+ hd -> interface.haddr,
+ hd -> interface.hlen);
+
+ /* If there isn't already a host decl matching this
+ address, add it to the hash table. */
+ if (!hp) {
+ add_hash (host_hw_addr_hash,
+ hd -> interface.haddr, hd -> interface.hlen,
+ (unsigned char *)hd);
+ hd -> refcnt++; /* XXX */
+ }
+ }
+
+ /* If there was already a host declaration for this hardware
+ address, add this one to the end of the list. */
+
+ if (hp) {
+ for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr)
+ ;
+ np -> n_ipaddr = hd;
+ hd -> refcnt++; /* XXX */
+ }
+
+ /* 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 option_state *)0,
+ (struct option_state *)0,
+ esp -> data.option);
+ break;
+ }
+ }
+
+ /* If we got a client identifier, hash this entry by
+ client identifier. */
+ if (hd -> client_identifier.len) {
+ /* 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 ();
+ if (!host_uid_hash)
+ log_fatal ("Can't allocate host/uid hash");
+ hp = (struct host_decl *)0;
+ } else
+ hp = ((struct host_decl *)
+ hash_lookup (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len));
+
+ /* If there's already a host declaration for this
+ client identifier, add this one to the end of the
+ list. Otherwise, add it to the hash table. */
+ if (hp) {
+ /* Don't link it in twice... */
+ if (!np) {
+ for (np = hp; np -> n_ipaddr;
+ np = np -> n_ipaddr)
+ ;
+ np -> n_ipaddr = hd;
+ hd -> refcnt++; /* XXX */
+ }
+ } else {
+ add_hash (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len,
+ (unsigned char *)hd);
+ hd -> refcnt++; /* XXX */
+ }
+ }
+
+ if (dynamicp && commit) {
+ write_host (hd);
+ commit_leases ();
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void delete_host (hd, commit)
+ struct host_decl *hd;
+ int commit;
+{
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *np = (struct host_decl *)0;
+ struct executable_statement *esp;
+ int hw_head = 0, uid_head = 1;
+
+ /* Don't need to do it twice. */
+ if (hd -> flags & HOST_DECL_DELETED)
+ return;
+
+ /* But we do need to do it once! :') */
+ hd -> flags |= HOST_DECL_DELETED;
+
+ /* If it's a dynamic entry, we write the deletion to the lease
+ file, but then we can delete the host from the list of dynamic
+ hosts, and next time the lease file is rewritten, the entry
+ will simply be left out. */
+ if (hd -> flags & HOST_DECL_DYNAMIC) {
+ for (hp = dynamic_hosts; hp; hp = np) {
+ np = hp -> n_dynamic;
+ if (hd == hp)
+ break;
+ }
+ if (hp) {
+ if (np)
+ np -> n_dynamic = hp -> n_dynamic;
+ else
+ dynamic_hosts = hp -> n_dynamic;
+ hp -> n_dynamic = (struct host_decl *)0;
+ }
+ np = (struct host_decl *)0;
+
+ } else {
+ /* If it's *not* a dynamic entry, then we have to remember
+ in perpetuity that it's been deleted. */
+ hd -> n_dynamic = dynamic_hosts;
+ dynamic_hosts = hd;
+ }
+
+ if (hd -> interface.hlen) {
+ if (host_hw_addr_hash) {
+ hp = (struct host_decl *)
+ hash_lookup (host_hw_addr_hash,
+ hd -> interface.haddr,
+ hd -> interface.hlen);
+
+ if (hp) {
+ if (hp == hd) {
+ delete_hash_entry (host_hw_addr_hash,
+ hd -> interface.haddr,
+ hd -> interface.hlen);
+ hw_head = 1;
+ --hd -> refcnt;
+ }
+ } else {
+ for (; hp; hp = hp -> n_ipaddr) {
+ np = hp;
+ if (hp == hd)
+ break;
+ }
+ if (hp) {
+ np -> n_ipaddr = hd -> n_ipaddr;
+ hd -> refcnt--;
+ }
+ }
+ }
+ }
+
+ /* If we got a client identifier, hash this entry by
+ client identifier. */
+ if (hd -> client_identifier.len) {
+ if (host_uid_hash) {
+ hp = (struct host_decl *)
+ hash_lookup (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len);
+
+ if (hp) {
+ if (hp == hd) {
+ delete_hash_entry
+ (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len);
+ uid_head = 1;
+ --hd -> refcnt;
+ }
+ } else {
+ for (; hp; hp = hp -> n_ipaddr) {
+ np = hp;
+ if (hp == hd)
+ break;
+ }
+ if (hp) {
+ np -> n_ipaddr = hd -> n_ipaddr;
+ hd -> refcnt--;
+ }
+ }
+ }
+ }
+
+ if (hd -> n_ipaddr) {
+ if (uid_head && hd -> n_ipaddr -> client_identifier.len) {
+ add_hash (host_uid_hash,
+ hd -> n_ipaddr -> client_identifier.data,
+ hd -> n_ipaddr -> client_identifier.len,
+ (unsigned char *)hd -> n_ipaddr);
+ hd -> n_ipaddr -> refcnt++;
+ }
+ if (hw_head && hd -> n_ipaddr -> interface.hlen) {
+ add_hash (host_hw_addr_hash,
+ hd -> n_ipaddr -> interface.haddr,
+ hd -> n_ipaddr -> interface.hlen,
+ (unsigned char *)hd -> n_ipaddr);
+ hd -> n_ipaddr -> refcnt++;
+ }
+ omapi_object_dereference ((omapi_object_t **)&hd -> n_ipaddr,
+ "delete_host");
+ }
+
+ if (host_name_hash) {
+ hp = (struct host_decl *)
+ hash_lookup (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name));
+
+ if (hp) {
+ if (hp == hd) {
+ delete_hash_entry (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name));
+ --hd -> refcnt;
+ }
+ }
+ }
+
+ if (commit) {
+ write_host (hd);
+ commit_leases ();
+ }
+}
+
+struct host_decl *find_hosts_by_haddr (htype, haddr, hlen)
+ int htype;
+ unsigned char *haddr;
+ int hlen;
+{
+ struct host_decl *foo;
+
+ foo = (struct host_decl *)hash_lookup (host_hw_addr_hash,
+ haddr, hlen);
+ return foo;
+}
+
+struct host_decl *find_hosts_by_uid (data, len)
+ unsigned char *data;
+ int len;
+{
+ struct host_decl *foo;
+
+ foo = (struct host_decl *)hash_lookup (host_uid_hash, data, len);
+ return foo;
+}
+
+/* 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
+ list of addresses, looking for an address that's in the same shared
+ network as the one specified. Store the matching address through
+ the addr pointer, update the host pointer to point at the host_decl
+ that matched, and return the subnet that matched. */
+
+struct subnet *find_host_for_network (host, addr, share)
+ 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;
+
+ memset (&fixed_addr, 0, sizeof fixed_addr);
+
+ for (hp = *host; hp; hp = hp -> n_ipaddr) {
+ if (!hp -> fixed_addr)
+ continue;
+ if (!evaluate_data_expression (&fixed_addr, (struct packet *)0,
+ (struct lease *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ hp -> fixed_addr -> expression))
+ continue;
+ for (i = 0; i < fixed_addr.len; i += 4) {
+ ip_address.len = 4;
+ memcpy (ip_address.iabuf,
+ fixed_addr.data + i, 4);
+ subnet = find_grouped_subnet (share, ip_address);
+ if (subnet) {
+ *addr = ip_address;
+ *host = hp;
+ data_string_forget (&fixed_addr,
+ "find_host_for_network");
+ return subnet;
+ }
+ }
+ data_string_forget (&fixed_addr, "find_host_for_network");
+ }
+ return (struct subnet *)0;
+}
+
+void delete_group (struct group_object *group, int writep)
+{
+ struct group_object *g, *d;
+
+ /* If it's dynamic, and we're deleting it, we can remove it from
+ the list of dynamically-created groups. */
+ if (group -> n_dynamic && (group -> flags & GROUP_OBJECT_DYNAMIC)) {
+ g = (struct group_object *)0;
+ omapi_object_reference ((omapi_object_t **)&g,
+ (omapi_object_t *)group -> n_dynamic,
+ "delete_group");
+ omapi_object_dereference ((omapi_object_t **)
+ &group -> n_dynamic, "delete_group");
+
+ if (group != groups) {
+ for (d = groups; d; d = d -> n_dynamic)
+ if (d -> n_dynamic == group)
+ break;
+ if (d) {
+ omapi_object_dereference
+ ((omapi_object_t **)&d -> n_dynamic,
+ "delete_group");
+ omapi_object_reference
+ ((omapi_object_t **)&d -> n_dynamic,
+ (omapi_object_t *)g,
+ "delete_group");
+ }
+ } else {
+ omapi_object_dereference ((omapi_object_t **)&groups,
+ "delete_group");
+ omapi_object_reference ((omapi_object_t **)&groups,
+ (omapi_object_t *)g,
+ "delete_group");
+ }
+ omapi_object_dereference ((omapi_object_t **)&g,
+ "delete_group");
+
+ /* We can also get rid of the hash entry now. */
+ d = ((struct group_object *)
+ hash_lookup (group_name_hash,
+ group -> name, strlen (group -> name)));
+ if (d) {
+ delete_hash_entry (group_name_hash,
+ group -> name,
+ strlen (group -> name));
+ --group -> refcnt;
+ }
+ }
+
+ /* Conversely, if it's not dynamic, and we're deleting it,
+ then we need to _add_ it to the list of dynamic hosts,
+ because we're recording the erasure of something that's
+ (for all we know, anyway) on permanent record in the
+ dhcpd.conf file. */
+ if (!group -> n_dynamic &&
+ group != groups && !(group -> flags & GROUP_OBJECT_DYNAMIC)) {
+ omapi_object_reference ((omapi_object_t **)&group -> n_dynamic,
+ (omapi_object_t *)groups,
+ "delete_group");
+ omapi_object_dereference ((omapi_object_t **)&groups,
+ "delete_group");
+ omapi_object_reference ((omapi_object_t **)&groups,
+ (omapi_object_t *)group,
+ "delete_group");
+ }
+
+ /* Store the group declaration in the lease file. */
+ if (writep)
+ write_group (group);
+}
+
+isc_result_t supersede_group (struct group_object *group, int writep)
+{
+ struct group_object *t;
+ isc_result_t status;
+
+ /* Register the group in the group name hash table,
+ so we can look it up later. */
+ if (group_name_hash) {
+ t = ((struct group_object *)
+ hash_lookup (group_name_hash,
+ group -> name,
+ strlen (group -> name)));
+ if (t && t != group) {
+ /* If this isn't a dynamic entry, then we need to flag
+ the replacement as not dynamic either - otherwise,
+ if the dynamic entry is deleted later, the static
+ entry will come back next time the server is stopped
+ and restarted. */
+ if (!(t -> flags & GROUP_OBJECT_DYNAMIC))
+ group -> flags |= GROUP_OBJECT_STATIC;
+
+ /* Delete the old object if it hasn't already been
+ deleted. If it has already been deleted, get rid of
+ the hash table entry. This is a legitimate
+ situation - a deleted static object needs to be kept
+ around so we remember it's deleted. */
+ if (!(t -> flags & GROUP_OBJECT_DELETED))
+ delete_group (t, 0);
+ else {
+ delete_hash_entry (group_name_hash,
+ group -> name,
+ strlen (group -> name));
+ omapi_object_dereference
+ ((omapi_object_t **)&t,
+ "supersede_group");
+ }
+ }
+ } else {
+ group_name_hash = new_hash ();
+ t = (struct group_object *)0;
+ }
+
+ /* Add the group to the group name hash if it's not
+ already there, and also thread it into the list of
+ dynamic groups if appropriate. */
+ if (!t) {
+ add_hash (group_name_hash,
+ group -> name, strlen (group -> name),
+ (unsigned char *)group);
+ }
+
+ /* Store the group declaration in the lease file. */
+ if (writep) {
+ status = write_group (group);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void new_address_range (low, high, subnet, pool)
+ struct iaddr low, high;
+ struct subnet *subnet;
+ struct pool *pool;
+{
+ struct lease *address_range, *lp, *plp;
+ struct iaddr net;
+ int min, max, i;
+ char lowbuf [16], highbuf [16], netbuf [16];
+ struct shared_network *share = subnet -> shared_network;
+
+ /* All subnets should have attached shared network structures. */
+ if (!share) {
+ strcpy (netbuf, piaddr (subnet -> net));
+ log_fatal ("No shared network for network %s (%s)",
+ netbuf, piaddr (subnet -> netmask));
+ }
+
+ /* Initialize the hash table if it hasn't been done yet. */
+ if (!lease_uid_hash) {
+ lease_uid_hash = new_hash ();
+ if (!lease_uid_hash)
+ log_fatal ("Can't allocate lease/uid hash");
+ }
+ if (!lease_ip_addr_hash) {
+ lease_ip_addr_hash = new_hash ();
+ if (!lease_uid_hash)
+ log_fatal ("Can't allocate lease/ip hash");
+ }
+ if (!lease_hw_addr_hash) {
+ lease_hw_addr_hash = new_hash ();
+ if (!lease_uid_hash)
+ 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 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);
+ }
+
+ /* Get the high and low host addresses... */
+ max = host_addr (high, subnet -> netmask);
+ min = host_addr (low, subnet -> netmask);
+
+ /* Allow range to be specified high-to-low as well as low-to-high. */
+ if (min > max) {
+ max = min;
+ min = host_addr (high, subnet -> netmask);
+ }
+
+ /* Get a lease structure for each address in the range. */
+ address_range = new_leases (max - min + 1, "new_address_range");
+ if (!address_range) {
+ strcpy (lowbuf, piaddr (low));
+ strcpy (highbuf, piaddr (high));
+ log_fatal ("No memory for address range %s-%s.",
+ lowbuf, highbuf);
+ }
+ memset (address_range, 0, (sizeof *address_range) * (max - min + 1));
+
+ /* Fill in the last lease if it hasn't been already... */
+ if (!pool -> last_lease) {
+ pool -> last_lease = &address_range [0];
+ }
+
+ /* Fill out the lease structures with some minimal information. */
+ for (i = 0; i < max - min + 1; i++) {
+ address_range [i].ip_addr =
+ ip_addr (subnet -> net, subnet -> netmask, i + min);
+ address_range [i].starts =
+ address_range [i].timestamp = MIN_TIME;
+ address_range [i].ends = MIN_TIME;
+ address_range [i].subnet = subnet;
+ address_range [i].pool = pool;
+ address_range [i].billing_class = (struct class *)0;
+ address_range [i].flags = 0;
+
+ /* Link this entry into the list. */
+ address_range [i].next = pool -> leases;
+ address_range [i].prev = (struct lease *)0;
+ pool -> leases = &address_range [i];
+ if (address_range [i].next)
+ address_range [i].next -> prev = pool -> leases;
+ add_hash (lease_ip_addr_hash,
+ address_range [i].ip_addr.iabuf,
+ address_range [i].ip_addr.len,
+ (unsigned char *)&address_range [i]);
+ }
+
+ /* Find out if any dangling leases are in range... */
+ plp = (struct lease *)0;
+ for (lp = dangling_leases; lp; lp = lp -> next) {
+ struct iaddr lnet;
+ int lhost;
+
+ lnet = subnet_number (lp -> ip_addr, subnet -> netmask);
+ lhost = host_addr (lp -> ip_addr, subnet -> netmask);
+
+ /* If it's in range, fill in the real lease structure with
+ the dangling lease's values, and remove the lease from
+ the list of dangling leases. */
+ if (addr_eq (lnet, subnet -> net) &&
+ lhost >= i && lhost <= max) {
+ if (plp) {
+ plp -> next = lp -> next;
+ } else {
+ dangling_leases = lp -> next;
+ }
+ lp -> next = (struct lease *)0;
+ address_range [lhost - i].hostname = lp -> hostname;
+ address_range [lhost - i].client_hostname =
+ lp -> client_hostname;
+ address_range [lhost - i].ddns_fwd_name =
+ lp -> ddns_fwd_name;
+ address_range [lhost - i].ddns_rev_name =
+ lp -> ddns_rev_name;
+ supersede_lease (&address_range [lhost - i], lp, 0);
+ free_lease (lp, "new_address_range");
+ } else
+ plp = lp;
+ }
+}
+
+struct subnet *find_subnet (addr)
+ struct iaddr addr;
+{
+ struct subnet *rv;
+
+ for (rv = subnets; rv; rv = rv -> next_subnet) {
+ if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net))
+ return rv;
+ }
+ return (struct subnet *)0;
+}
+
+struct subnet *find_grouped_subnet (share, addr)
+ struct shared_network *share;
+ struct iaddr addr;
+{
+ struct subnet *rv;
+
+ for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
+ if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net))
+ return rv;
+ }
+ return (struct subnet *)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];
+ int i, j;
+ for (i = 0; i < 32; 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)] &
+ (1 << (j & 7)))
+ break;
+ strcpy (n1buf, piaddr (subnet -> net));
+ if (warnp)
+ log_error ("%ssubnet %s/%d conflicts with subnet %s/%d",
+ "Warning: ", n1buf, 32 - i,
+ piaddr (scan -> net), 32 - j);
+ if (i < j)
+ return 1;
+ }
+ return 0;
+}
+
+/* Enter a new subnet into the subnet list. */
+void enter_subnet (subnet)
+ struct subnet *subnet;
+{
+ struct subnet *scan, *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) {
+ prev -> next_subnet = subnet;
+ } else
+ subnets = subnet;
+ subnet -> next_subnet = scan;
+ return;
+ }
+ prev = scan;
+ }
+
+ /* XXX use the BSD radix tree code instead of a linked list. */
+ subnet -> next_subnet = subnets;
+ subnets = subnet;
+}
+
+/* Enter a new shared network into the shared network list. */
+
+void enter_shared_network (share)
+ struct shared_network *share;
+{
+ /* XXX Sort the nets into a balanced tree to make searching quicker. */
+ share -> next = shared_networks;
+ shared_networks = share;
+}
+
+/* Enter a lease into the system. This is called by the parser each
+ time it reads in a new lease. If the subnet for that lease has
+ already been read in (usually the case), just update that lease;
+ otherwise, allocate temporary storage for the lease and keep it around
+ until we're done reading in the config file. */
+
+void enter_lease (lease)
+ struct lease *lease;
+{
+ struct lease *comp = find_lease_by_ip_addr (lease -> ip_addr);
+
+ /* If we don't have a place for this lease yet, save it for
+ later. */
+ if (!comp) {
+ comp = new_lease ("enter_lease");
+ if (!comp) {
+ log_fatal ("No memory for lease %s\n",
+ piaddr (lease -> ip_addr));
+ }
+ memset (comp, 0, sizeof *comp);
+ *comp = *lease;
+ comp -> next = dangling_leases;
+ comp -> prev = (struct lease *)0;
+ dangling_leases = comp;
+ } else {
+ /* Record the hostname information in the lease. */
+ comp -> hostname = lease -> hostname;
+ comp -> client_hostname = lease -> client_hostname;
+ comp -> ddns_fwd_name = lease -> ddns_fwd_name;
+ comp -> ddns_rev_name = lease -> ddns_rev_name;
+ supersede_lease (comp, lease, 0);
+ }
+}
+
+/* Replace the data in an existing lease with the data in a new lease;
+ adjust hash tables to suit, and insertion sort the lease into the
+ list of leases by expiry time so that we can always find the oldest
+ lease. */
+
+int supersede_lease (comp, lease, commit)
+ struct lease *comp, *lease;
+ int commit;
+{
+ int enter_uid = 0;
+ int enter_hwaddr = 0;
+ struct lease *lp;
+
+ /* Static leases are not currently kept in the database... */
+ if (lease -> flags & STATIC_LEASE)
+ return 1;
+
+ /* If the existing lease hasn't expired and has a different
+ unique identifier or, if it doesn't have a unique
+ identifier, a different hardware address, then the two
+ leases are in conflict. If the existing lease has a uid
+ and the new one doesn't, but they both have the same
+ hardware address, and dynamic bootp is allowed on this
+ lease, then we allow that, in case a dynamic BOOTP lease is
+ requested *after* a DHCP lease has been assigned. */
+
+ if (!(lease -> flags & ABANDONED_LEASE) &&
+ comp -> ends > cur_time &&
+ (((comp -> uid && lease -> uid) &&
+ (comp -> uid_len != lease -> uid_len ||
+ memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
+ (!comp -> uid &&
+ ((comp -> hardware_addr.htype !=
+ lease -> hardware_addr.htype) ||
+ (comp -> hardware_addr.hlen !=
+ lease -> hardware_addr.hlen) ||
+ memcmp (comp -> hardware_addr.haddr,
+ lease -> hardware_addr.haddr,
+ 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]) {
+ free (comp -> uid);
+ comp -> uid_max = 0;
+ comp -> uid_len = 0;
+ }
+ comp -> uid = (unsigned char *)0;
+ } else
+ enter_uid = 1;
+
+ if (comp -> hardware_addr.htype &&
+ ((comp -> hardware_addr.hlen !=
+ lease -> hardware_addr.hlen) ||
+ (comp -> hardware_addr.htype !=
+ lease -> hardware_addr.htype) ||
+ memcmp (comp -> hardware_addr.haddr,
+ lease -> hardware_addr.haddr,
+ comp -> hardware_addr.hlen))) {
+ hw_hash_delete (comp);
+ enter_hwaddr = 1;
+ } else if (!comp -> hardware_addr.htype)
+ enter_hwaddr = 1;
+
+ /* If the lease has been billed to a class, remove the billing. */
+ if (comp -> billing_class &&
+ comp -> billing_class != lease -> billing_class)
+ unbill_class (comp, comp -> billing_class);
+
+ /* Copy the data files, but not the linkages. */
+ comp -> starts = lease -> starts;
+ if (lease -> uid) {
+ if (lease -> uid_len < sizeof (lease -> uid_buf)) {
+ memcpy (comp -> uid_buf,
+ lease -> uid, lease -> uid_len);
+ comp -> uid = &comp -> uid_buf [0];
+ comp -> uid_max = sizeof comp -> uid_buf;
+ } else if (lease -> uid != &lease -> uid_buf [0]) {
+ comp -> uid = lease -> uid;
+ comp -> uid_max = lease -> uid_max;
+ lease -> uid = (unsigned char *)0;
+ lease -> uid_max = 0;
+ } else {
+ log_fatal ("corrupt lease uid."); /* XXX */
+ }
+ } else {
+ comp -> uid = (unsigned char *)0;
+ comp -> uid_max = 0;
+ }
+ comp -> uid_len = lease -> uid_len;
+ comp -> host = lease -> host;
+ comp -> hardware_addr = lease -> hardware_addr;
+ comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) |
+ (comp -> flags & ~EPHEMERAL_FLAGS));
+
+ if (lease -> on_expiry) {
+ if (comp -> on_expiry)
+ executable_statement_dereference (&comp -> on_expiry,
+ "supersede_lease");
+ executable_statement_reference (&comp -> on_expiry,
+ lease -> on_expiry,
+ "supersede_lease");
+ }
+ if (lease -> on_commit) {
+ if (comp -> on_commit)
+ executable_statement_dereference (&comp -> on_commit,
+ "supersede_lease");
+ executable_statement_reference (&comp -> on_commit,
+ lease -> on_commit,
+ "supersede_lease");
+ }
+ if (lease -> on_release) {
+ if (comp -> on_release)
+ executable_statement_dereference (&comp -> on_release,
+ "supersede_lease");
+ executable_statement_reference (&comp -> on_release,
+ lease -> on_release,
+ "supersede_lease");
+ }
+
+ /* Record the lease in the uid hash if necessary. */
+ if (enter_uid && lease -> uid) {
+ uid_hash_add (comp);
+ }
+
+ /* Record it in the hardware address hash if necessary. */
+ if (enter_hwaddr && lease -> hardware_addr.htype) {
+ hw_hash_add (comp);
+ }
+
+ /* Remove the lease from its current place in the
+ timeout sequence. */
+ if (comp -> prev) {
+ comp -> prev -> next = comp -> next;
+ } else {
+ comp -> pool -> leases = comp -> next;
+ }
+ if (comp -> next) {
+ comp -> next -> prev = comp -> prev;
+ }
+ if (comp -> pool -> last_lease == comp) {
+ comp -> pool -> last_lease = comp -> prev;
+ }
+
+ /* Find the last insertion point... */
+ if (comp == comp -> pool -> insertion_point ||
+ !comp -> pool -> insertion_point) {
+ lp = comp -> pool -> leases;
+ } else {
+ lp = comp -> pool -> insertion_point;
+ }
+
+ if (!lp) {
+ /* Nothing on the list yet? Just make comp the
+ head of the list. */
+ comp -> pool -> leases = comp;
+ comp -> pool -> last_lease = comp;
+ } else if (lp -> ends > lease -> ends) {
+ /* Skip down the list until we run out of list
+ or find a place for comp. */
+ while (lp -> next && lp -> ends > lease -> ends) {
+ lp = lp -> next;
+ }
+ if (lp -> ends > lease -> ends) {
+ /* If we ran out of list, put comp at the end. */
+ lp -> next = comp;
+ comp -> prev = lp;
+ comp -> next = (struct lease *)0;
+ comp -> pool -> last_lease = comp;
+ } else {
+ /* If we didn't, put it between lp and
+ the previous item on the list. */
+ if ((comp -> prev = lp -> prev))
+ comp -> prev -> next = comp;
+ comp -> next = lp;
+ lp -> prev = comp;
+ }
+ } else {
+ /* Skip up the list until we run out of list
+ or find a place for comp. */
+ while (lp -> prev && lp -> ends < lease -> ends) {
+ lp = lp -> prev;
+ }
+ if (lp -> ends < lease -> ends) {
+ /* If we ran out of list, put comp at the beginning. */
+ lp -> prev = comp;
+ comp -> next = lp;
+ comp -> prev = (struct lease *)0;
+ comp -> pool -> leases = comp;
+ } else {
+ /* If we didn't, put it between lp and
+ the next item on the list. */
+ if ((comp -> next = lp -> next))
+ comp -> next -> prev = comp;
+ comp -> prev = lp;
+ lp -> next = comp;
+ }
+ }
+ comp -> pool -> insertion_point = comp;
+ comp -> ends = lease -> ends;
+
+ /* Return zero if we didn't commit the lease to permanent storage;
+ nonzero if we did. */
+ return commit && write_lease (comp) && commit_leases ();
+}
+
+/* Release the specified lease and re-hash it as appropriate. */
+
+void release_lease (lease)
+ struct lease *lease;
+{
+ struct lease lt;
+
+ lt = *lease;
+ if (lt.ends > cur_time) {
+ lt.ends = cur_time;
+ lt.billing_class = (struct class *)0;
+ supersede_lease (lease, <, 1);
+ }
+}
+
+/* Abandon the specified lease (set its timeout to infinity and its
+ particulars to zero, and re-hash it as appropriate. */
+
+void abandon_lease (lease, message)
+ struct lease *lease;
+ char *message;
+{
+ struct lease lt;
+
+ lease -> flags |= ABANDONED_LEASE;
+ lt = *lease;
+ lt.ends = cur_time; /* XXX */
+ log_error ("Abandoning IP address %s: %s",
+ piaddr (lease -> ip_addr), message);
+ lt.hardware_addr.htype = 0;
+ lt.hardware_addr.hlen = 0;
+ lt.uid = (unsigned char *)0;
+ lt.uid_len = 0;
+ lt.billing_class = (struct class *)0;
+ supersede_lease (lease, <, 1);
+}
+
+/* Abandon the specified lease (set its timeout to infinity and its
+ particulars to zero, and re-hash it as appropriate. */
+
+void dissociate_lease (lease)
+ struct lease *lease;
+{
+ struct lease lt;
+
+ lt = *lease;
+ if (lt.ends > cur_time)
+ lt.ends = cur_time;
+ lt.hardware_addr.htype = 0;
+ lt.hardware_addr.hlen = 0;
+ lt.uid = (unsigned char *)0;
+ lt.uid_len = 0;
+ lt.billing_class = (struct class *)0;
+ supersede_lease (lease, <, 1);
+}
+
+/* Locate the lease associated with a given IP address... */
+
+struct lease *find_lease_by_ip_addr (addr)
+ struct iaddr addr;
+{
+ struct lease *lease = (struct lease *)hash_lookup (lease_ip_addr_hash,
+ addr.iabuf,
+ addr.len);
+ return lease;
+}
+
+struct lease *find_lease_by_uid (uid, len)
+ unsigned char *uid;
+ int len;
+{
+ struct lease *lease = (struct lease *)hash_lookup (lease_uid_hash,
+ uid, len);
+ return lease;
+}
+
+struct lease *find_lease_by_hw_addr (hwaddr, hwlen)
+ unsigned char *hwaddr;
+ int hwlen;
+{
+ struct lease *lease = (struct lease *)hash_lookup (lease_hw_addr_hash,
+ hwaddr, hwlen);
+ return lease;
+}
+
+/* Add the specified lease to the uid hash. */
+
+void uid_hash_add (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_uid (lease -> uid, lease -> uid_len);
+ struct lease *scan;
+
+ /* If it's not in the hash, just add it. */
+ if (!head)
+ add_hash (lease_uid_hash, lease -> uid,
+ lease -> uid_len, (unsigned char *)lease);
+ else {
+ /* Otherwise, attach it to the end of the list. */
+ for (scan = head; scan -> n_uid; scan = scan -> n_uid)
+ ;
+ scan -> n_uid = lease;
+ }
+}
+
+/* Delete the specified lease from the uid hash. */
+
+void uid_hash_delete (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_uid (lease -> uid, lease -> uid_len);
+ struct lease *scan;
+
+ /* If it's not in the hash, we have no work to do. */
+ if (!head) {
+ lease -> n_uid = (struct lease *)0;
+ return;
+ }
+
+ /* If the lease we're freeing is at the head of the list,
+ remove the hash table entry and add a new one with the
+ next lease on the list (if there is one). */
+ if (head == lease) {
+ delete_hash_entry (lease_uid_hash,
+ lease -> uid, lease -> uid_len);
+ if (lease -> n_uid)
+ add_hash (lease_uid_hash,
+ lease -> n_uid -> uid,
+ lease -> n_uid -> uid_len,
+ (unsigned char *)(lease -> n_uid));
+ } else {
+ /* Otherwise, look for the lease in the list of leases
+ attached to the hash table entry, and remove it if
+ we find it. */
+ for (scan = head; scan -> n_uid; scan = scan -> n_uid) {
+ if (scan -> n_uid == lease) {
+ scan -> n_uid = scan -> n_uid -> n_uid;
+ break;
+ }
+ }
+ }
+ lease -> n_uid = (struct lease *)0;
+}
+
+/* Add the specified lease to the hardware address hash. */
+
+void hw_hash_add (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_hw_addr (lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen);
+ struct lease *scan;
+
+ /* If it's not in the hash, just add it. */
+ if (!head)
+ add_hash (lease_hw_addr_hash,
+ lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen,
+ (unsigned char *)lease);
+ else {
+ /* Otherwise, attach it to the end of the list. */
+ for (scan = head; scan -> n_hw; scan = scan -> n_hw)
+ ;
+ scan -> n_hw = lease;
+ }
+}
+
+/* Delete the specified lease from the hardware address hash. */
+
+void hw_hash_delete (lease)
+ struct lease *lease;
+{
+ struct lease *head =
+ find_lease_by_hw_addr (lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen);
+ struct lease *scan;
+
+ /* If it's not in the hash, we have no work to do. */
+ if (!head) {
+ lease -> n_hw = (struct lease *)0;
+ return;
+ }
+
+ /* If the lease we're freeing is at the head of the list,
+ remove the hash table entry and add a new one with the
+ next lease on the list (if there is one). */
+ if (head == lease) {
+ delete_hash_entry (lease_hw_addr_hash,
+ lease -> hardware_addr.haddr,
+ lease -> hardware_addr.hlen);
+ if (lease -> n_hw)
+ add_hash (lease_hw_addr_hash,
+ lease -> n_hw -> hardware_addr.haddr,
+ lease -> n_hw -> hardware_addr.hlen,
+ (unsigned char *)(lease -> n_hw));
+ } else {
+ /* Otherwise, look for the lease in the list of leases
+ attached to the hash table entry, and remove it if
+ we find it. */
+ for (scan = head; scan -> n_hw; scan = scan -> n_hw) {
+ if (scan -> n_hw == lease) {
+ scan -> n_hw = scan -> n_hw -> n_hw;
+ break;
+ }
+ }
+ }
+ lease -> n_hw = (struct lease *)0;
+}
+
+/* Write all interesting leases to permanent storage. */
+
+void write_leases ()
+{
+ struct lease *l;
+ struct shared_network *s;
+ struct pool *p;
+ struct host_decl *hp;
+ struct group_object *gp;
+
+ /* Write all the dynamically-created host declarations. */
+ for (hp = dynamic_hosts; hp; hp = hp -> n_dynamic)
+ write_host (hp);
+
+ /* Write all the dynamically-created group declarations. */
+ for (gp = groups; gp; gp = gp -> n_dynamic)
+ write_group (gp);
+
+ /* Write all the leases. */
+ for (s = shared_networks; s; s = s -> next) {
+ for (p = s -> pools; p; p = p -> next) {
+ for (l = p -> leases; l; l = l -> next) {
+ if (l -> hardware_addr.hlen ||
+ l -> uid_len ||
+ (l -> flags & ABANDONED_LEASE))
+ if (!write_lease (l))
+ log_fatal ("Can't rewrite %s",
+ "lease database");
+ }
+ }
+ }
+ if (!commit_leases ())
+ log_fatal ("Can't commit leases to new database: %m");
+}
+
+void dump_subnets ()
+{
+ struct lease *l;
+ struct shared_network *s;
+ struct subnet *n;
+ struct pool *p;
+
+ log_info ("Subnets:");
+ for (n = subnets; n; n = n -> next_subnet) {
+ log_debug (" Subnet %s", piaddr (n -> net));
+ log_debug (" netmask %s",
+ piaddr (n -> netmask));
+ }
+ log_info ("Shared networks:");
+ for (s = shared_networks; s; s = s -> next) {
+ log_info (" %s", s -> name);
+ for (p = s -> pools; p; p = p -> next) {
+ for (l = p -> leases; l; l = l -> next) {
+ print_lease (l);
+ }
+ log_debug ("Last Lease:");
+ print_lease (p -> last_lease);
+ }
+ }
+}