xref: /netbsd/external/mpl/dhcp/dist/server/dhcpd.c (revision 13df4856)
1*13df4856Schristos /*	$NetBSD: dhcpd.c,v 1.5 2022/04/03 01:10:59 christos Exp $	*/
23965be93Schristos 
33965be93Schristos /* dhcpd.c
43965be93Schristos 
53965be93Schristos    DHCP Server Daemon. */
63965be93Schristos 
73965be93Schristos /*
8*13df4856Schristos  * Copyright (c) 2004-2022 by Internet Systems Consortium, Inc. ("ISC")
93965be93Schristos  * Copyright (c) 1996-2003 by Internet Software Consortium
103965be93Schristos  *
113965be93Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
123965be93Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
133965be93Schristos  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
143965be93Schristos  *
153965be93Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
163965be93Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
173965be93Schristos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
183965be93Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
193965be93Schristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
203965be93Schristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
213965be93Schristos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
223965be93Schristos  *
233965be93Schristos  *   Internet Systems Consortium, Inc.
24*13df4856Schristos  *   PO Box 360
25*13df4856Schristos  *   Newmarket, NH 03857 USA
263965be93Schristos  *   <info@isc.org>
273965be93Schristos  *   https://www.isc.org/
283965be93Schristos  *
293965be93Schristos  */
303965be93Schristos 
313965be93Schristos #include <sys/cdefs.h>
32*13df4856Schristos __RCSID("$NetBSD: dhcpd.c,v 1.5 2022/04/03 01:10:59 christos Exp $");
333965be93Schristos 
343965be93Schristos static const char copyright[] =
35*13df4856Schristos "Copyright 2004-2022 Internet Systems Consortium.";
363965be93Schristos static const char arr [] = "All rights reserved.";
373965be93Schristos static const char message [] = "Internet Systems Consortium DHCP Server";
383965be93Schristos static const char url [] =
393965be93Schristos "For info, please visit https://www.isc.org/software/dhcp/";
403965be93Schristos 
413965be93Schristos #include "dhcpd.h"
423965be93Schristos #include <omapip/omapip_p.h>
433965be93Schristos #include <syslog.h>
443965be93Schristos #include <signal.h>
453965be93Schristos #include <errno.h>
463965be93Schristos #include <limits.h>
473965be93Schristos #include <sys/types.h>
483965be93Schristos #include <sys/time.h>
493965be93Schristos #include <isc/file.h>
503965be93Schristos 
513965be93Schristos #if defined (PARANOIA)
523965be93Schristos #  include <sys/types.h>
533965be93Schristos #  include <unistd.h>
543965be93Schristos #  include <pwd.h>
553965be93Schristos /* get around the ISC declaration of group */
563965be93Schristos #  define group real_group
573965be93Schristos #    include <grp.h>
583965be93Schristos #  undef group
593965be93Schristos 
603965be93Schristos /* global values so db.c can look at them */
613965be93Schristos uid_t set_uid = 0;
623965be93Schristos gid_t set_gid = 0;
633965be93Schristos #endif /* PARANOIA */
643965be93Schristos 
653965be93Schristos struct class unknown_class;
663965be93Schristos struct class known_class;
673965be93Schristos 
683965be93Schristos struct iaddr server_identifier;
693965be93Schristos int server_identifier_matched;
703965be93Schristos 
713965be93Schristos #if defined (NSUPDATE)
723965be93Schristos 
733965be93Schristos /* This stuff is always executed to figure the default values for certain
743965be93Schristos    ddns variables. */
753965be93Schristos char std_nsupdate [] = "						    \n\
763965be93Schristos option server.ddns-hostname =						    \n\
773965be93Schristos   pick (option fqdn.hostname, option host-name, config-option host-name);   \n\
783965be93Schristos option server.ddns-domainname =	config-option domain-name;		    \n\
793965be93Schristos option server.ddns-rev-domainname = \"in-addr.arpa.\";";
803965be93Schristos 
813965be93Schristos /* Stores configured DDNS conflict detection flags */
823965be93Schristos u_int16_t ddns_conflict_mask;
833965be93Schristos #endif /* NSUPDATE */
843965be93Schristos 
853965be93Schristos int ddns_update_style;
863965be93Schristos int dont_use_fsync = 0; /* 0 = default, use fsync, 1 = don't use fsync */
873965be93Schristos int server_id_check = 0; /* 0 = default, don't check server id, 1 = do check */
883965be93Schristos 
893965be93Schristos #ifdef DHCPv6
903965be93Schristos int prefix_length_mode = PLM_PREFER;
913965be93Schristos int do_release_on_roam = 0; /* 0 = default, do not release v6 leases on roam */
923965be93Schristos #endif
933965be93Schristos 
943965be93Schristos #ifdef EUI_64
953965be93Schristos int persist_eui64 = 1; /* 1 = write EUI64 leases to disk, 0 = don't */
963965be93Schristos #endif
973965be93Schristos 
983965be93Schristos int authoring_byte_order = 0; /* 0 = not set */
993965be93Schristos int lease_id_format = TOKEN_OCTAL; /* octal by default */
1003965be93Schristos u_int32_t abandon_lease_time = DEFAULT_ABANDON_LEASE_TIME;
1013965be93Schristos 
1023965be93Schristos const char *path_dhcpd_conf = _PATH_DHCPD_CONF;
1033965be93Schristos const char *path_dhcpd_db = _PATH_DHCPD_DB;
1043965be93Schristos const char *path_dhcpd_pid = _PATH_DHCPD_PID;
1053965be93Schristos /* False (default) => we write and use a pid file */
1063965be93Schristos isc_boolean_t no_pid_file = ISC_FALSE;
1073965be93Schristos 
1083965be93Schristos int dhcp_max_agent_option_packet_length = DHCP_MTU_MAX;
1093965be93Schristos 
1103965be93Schristos static omapi_auth_key_t *omapi_key = (omapi_auth_key_t *)0;
1113965be93Schristos int omapi_port;
1123965be93Schristos 
1133965be93Schristos #if defined (TRACING)
1143965be93Schristos trace_type_t *trace_srandom;
1153965be93Schristos #endif
1163965be93Schristos 
117*13df4856Schristos extern uint16_t local_port;
118*13df4856Schristos extern uint16_t remote_port;
11907146da4Schristos libdhcp_callbacks_t dhcpd_callbacks = {
12007146da4Schristos 	&local_port,
12107146da4Schristos 	&remote_port,
12207146da4Schristos 	classify,
12307146da4Schristos 	check_collection,
12407146da4Schristos 	dhcp,
12507146da4Schristos #ifdef DHCPv6
12607146da4Schristos 	dhcpv6,
12707146da4Schristos #endif /* DHCPv6 */
12807146da4Schristos 	bootp,
12907146da4Schristos 	find_class,
13007146da4Schristos 	parse_allow_deny,
13107146da4Schristos 	dhcp_set_control_state,
13207146da4Schristos };
13307146da4Schristos 
1343965be93Schristos char *progname;
1353965be93Schristos 
verify_addr(omapi_object_t * l,omapi_addr_t * addr)1363965be93Schristos static isc_result_t verify_addr (omapi_object_t *l, omapi_addr_t *addr) {
1373965be93Schristos 	return ISC_R_SUCCESS;
1383965be93Schristos }
1393965be93Schristos 
verify_auth(omapi_object_t * p,omapi_auth_key_t * a)1403965be93Schristos static isc_result_t verify_auth (omapi_object_t *p, omapi_auth_key_t *a) {
1413965be93Schristos 	if (a != omapi_key)
1423965be93Schristos 		return DHCP_R_INVALIDKEY;
1433965be93Schristos 	return ISC_R_SUCCESS;
1443965be93Schristos }
1453965be93Schristos 
omapi_listener_start(void * foo)1463965be93Schristos static void omapi_listener_start (void *foo)
1473965be93Schristos {
1483965be93Schristos 	omapi_object_t *listener;
1493965be93Schristos 	isc_result_t result;
1503965be93Schristos 	struct timeval tv;
1513965be93Schristos 
1523965be93Schristos 	listener = (omapi_object_t *)0;
1533965be93Schristos 	result = omapi_generic_new (&listener, MDL);
1543965be93Schristos 	if (result != ISC_R_SUCCESS)
1553965be93Schristos 		log_fatal ("Can't allocate new generic object: %s",
1563965be93Schristos 			   isc_result_totext (result));
1573965be93Schristos 	result = omapi_protocol_listen (listener,
1583965be93Schristos 					(unsigned)omapi_port, 1);
1593965be93Schristos 	if (result == ISC_R_SUCCESS && omapi_key)
1603965be93Schristos 		result = omapi_protocol_configure_security
1613965be93Schristos 			(listener, verify_addr, verify_auth);
1623965be93Schristos 	if (result != ISC_R_SUCCESS) {
1633965be93Schristos 		log_error ("Can't start OMAPI protocol: %s",
1643965be93Schristos 			   isc_result_totext (result));
1653965be93Schristos 		tv.tv_sec = cur_tv.tv_sec + 5;
1663965be93Schristos 		tv.tv_usec = cur_tv.tv_usec;
1673965be93Schristos 		add_timeout (&tv, omapi_listener_start, 0, 0, 0);
1683965be93Schristos 	}
1693965be93Schristos 	omapi_object_dereference (&listener, MDL);
1703965be93Schristos }
1713965be93Schristos 
1723965be93Schristos #ifndef UNIT_TEST
1733965be93Schristos 
1743965be93Schristos #define DHCPD_USAGE0 \
1753965be93Schristos "[-p <UDP port #>] [-f] [-d] [-q] [-t|-T]\n"
1763965be93Schristos 
1773965be93Schristos #ifdef DHCPv6
1783965be93Schristos #ifdef DHCP4o6
1793965be93Schristos #define DHCPD_USAGE1 \
1803965be93Schristos "             [-4|-6] [-4o6 <port>]\n" \
1813965be93Schristos "             [-cf config-file] [-lf lease-file]\n"
1823965be93Schristos #else /* DHCP4o6 */
1833965be93Schristos #define DHCPD_USAGE1 \
1843965be93Schristos "             [-4|-6] [-cf config-file] [-lf lease-file]\n"
1853965be93Schristos #endif /* DHCP4o6 */
1863965be93Schristos #else /* !DHCPv6 */
1873965be93Schristos #define DHCPD_USAGE1 \
1883965be93Schristos "             [-cf config-file] [-lf lease-file]\n"
1893965be93Schristos #endif /* DHCPv6 */
1903965be93Schristos 
1913965be93Schristos #if defined (PARANOIA)
1923965be93Schristos #define DHCPD_USAGEP \
1933965be93Schristos "             [-user user] [-group group] [-chroot dir]\n"
1943965be93Schristos #else
1953965be93Schristos #define DHCPD_USAGEP ""
1963965be93Schristos #endif /* PARANOIA */
1973965be93Schristos 
1983965be93Schristos #if defined (TRACING)
1993965be93Schristos #define DHCPD_USAGET \
2003965be93Schristos "             [-tf trace-output-file]\n" \
2013965be93Schristos "             [-play trace-input-file]\n"
2023965be93Schristos #else
2033965be93Schristos #define DHCPD_USAGET ""
2043965be93Schristos #endif /* TRACING */
2053965be93Schristos 
2063965be93Schristos #define DHCPD_USAGEC \
2073965be93Schristos "             [-pf pid-file] [--no-pid] [-s server]\n" \
2083965be93Schristos "             [if0 [...ifN]]"
2093965be93Schristos 
2103965be93Schristos #define DHCPD_USAGEH "{--version|--help|-h}"
2113965be93Schristos 
2123965be93Schristos /*!
2133965be93Schristos  *
2143965be93Schristos  * \brief Print the generic usage message
2153965be93Schristos  *
2163965be93Schristos  * If the user has provided an incorrect command line print out
2173965be93Schristos  * the description of the command line.  The arguments provide
2183965be93Schristos  * a way for the caller to request more specific information about
2193965be93Schristos  * the error be printed as well.  Mostly this will be that some
220*13df4856Schristos  * command doesn't include its argument.
2213965be93Schristos  *
2223965be93Schristos  * \param sfmt - The basic string and format for the specific error
223*13df4856Schristos  * \param sarg - Generally the offending argument from the command line.
2243965be93Schristos  *
2253965be93Schristos  * \return Nothing
2263965be93Schristos  */
2273965be93Schristos 
2283965be93Schristos #include <sys/cdefs.h>
229*13df4856Schristos __RCSID("$NetBSD: dhcpd.c,v 1.5 2022/04/03 01:10:59 christos Exp $");
2303965be93Schristos static char use_noarg[] = "No argument for command: %s ";
2313965be93Schristos 
2323965be93Schristos static void
usage(const char * sfmt,const char * sarg)2333965be93Schristos usage(const char *sfmt, const char *sarg) {
2343965be93Schristos 	log_info("%s %s", message, PACKAGE_VERSION);
2353965be93Schristos 	log_info(copyright);
2363965be93Schristos 	log_info(arr);
2373965be93Schristos 	log_info(url);
2383965be93Schristos 
2393965be93Schristos 	/* If desired print out the specific error message */
2403965be93Schristos #ifdef PRINT_SPECIFIC_CL_ERRORS
2413965be93Schristos 	if (sfmt != NULL)
2423965be93Schristos 		log_error(sfmt, sarg);
2433965be93Schristos #endif
2443965be93Schristos 
2453965be93Schristos 	log_fatal("Usage: %s %s%s%s%s%s\n       %s %s",
2463965be93Schristos 		  isc_file_basename(progname),
2473965be93Schristos 		  DHCPD_USAGE0,
2483965be93Schristos 		  DHCPD_USAGE1,
2493965be93Schristos 		  DHCPD_USAGEP,
2503965be93Schristos 		  DHCPD_USAGET,
2513965be93Schristos 		  DHCPD_USAGEC,
2523965be93Schristos 		  isc_file_basename(progname),
2533965be93Schristos 		  DHCPD_USAGEH);
2543965be93Schristos }
2553965be93Schristos 
2563965be93Schristos /* Note: If we add unit tests to test setup_chroot it will
2573965be93Schristos  * need to be moved to be outside the ifndef UNIT_TEST block.
2583965be93Schristos  */
2593965be93Schristos 
2603965be93Schristos #if defined (PARANOIA)
2613965be93Schristos /* to be used in one of two possible scenarios */
setup_chroot(char * chroot_dir)2623965be93Schristos static void setup_chroot (char *chroot_dir) {
2633965be93Schristos   if (geteuid())
2643965be93Schristos     log_fatal ("you must be root to use chroot");
2653965be93Schristos 
2663965be93Schristos   if (chroot(chroot_dir)) {
2673965be93Schristos     log_fatal ("chroot(\"%s\"): %m", chroot_dir);
2683965be93Schristos   }
2693965be93Schristos   if (chdir ("/")) {
2703965be93Schristos     /* probably permission denied */
2713965be93Schristos     log_fatal ("chdir(\"/\"): %m");
2723965be93Schristos   }
2733965be93Schristos }
2743965be93Schristos #endif /* PARANOIA */
2753965be93Schristos 
2763965be93Schristos int
main(int argc,char ** argv)2773965be93Schristos main(int argc, char **argv) {
2783965be93Schristos 	int fd;
2793965be93Schristos 	int i, status;
2803965be93Schristos 	struct servent *ent;
2813965be93Schristos 	char *s;
2823965be93Schristos 	int cftest = 0;
2833965be93Schristos 	int lftest = 0;
2843965be93Schristos 	int pid;
2853965be93Schristos 	char pbuf [20];
2863965be93Schristos #ifndef DEBUG
2873965be93Schristos 	int daemon = 1;
2883965be93Schristos 	int dfd[2] = { -1, -1 };
2893965be93Schristos #endif
2903965be93Schristos 	int quiet = 0;
2913965be93Schristos 	char *server = (char *)0;
2923965be93Schristos 	isc_result_t result;
2933965be93Schristos 	unsigned seed;
2943965be93Schristos 	struct interface_info *ip;
2953965be93Schristos #if defined (NSUPDATE)
2963965be93Schristos 	struct parse *parse;
2973965be93Schristos 	int lose;
2983965be93Schristos #endif
2993965be93Schristos 	int have_dhcpd_conf = 0;
3003965be93Schristos 	int have_dhcpd_db = 0;
3013965be93Schristos 	int have_dhcpd_pid = 0;
3023965be93Schristos #ifdef DHCPv6
3033965be93Schristos 	int local_family_set = 0;
3043965be93Schristos #ifdef DHCP4o6
3053965be93Schristos 	u_int16_t dhcp4o6_port = 0;
3063965be93Schristos #endif /* DHCP4o6 */
3073965be93Schristos #endif /* DHCPv6 */
3083965be93Schristos #if defined (TRACING)
3093965be93Schristos 	char *traceinfile = (char *)0;
3103965be93Schristos 	char *traceoutfile = (char *)0;
3113965be93Schristos #endif
3123965be93Schristos 
3133965be93Schristos #if defined (PARANOIA)
3143965be93Schristos 	char *set_user   = 0;
3153965be93Schristos 	char *set_group  = 0;
3163965be93Schristos 	char *set_chroot = 0;
3173965be93Schristos #endif /* PARANOIA */
3183965be93Schristos 
31907146da4Schristos 	libdhcp_callbacks_register(&dhcpd_callbacks);
32007146da4Schristos 
3213965be93Schristos #ifdef OLD_LOG_NAME
3223965be93Schristos 	progname = "dhcpd";
3233965be93Schristos #else
3243965be93Schristos 	progname = argv[0];
3253965be93Schristos #endif
3263965be93Schristos 
3273965be93Schristos         /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
3283965be93Schristos            2 (stderr) are open. To do this, we assume that when we
3293965be93Schristos            open a file the lowest available file descriptor is used. */
3303965be93Schristos         fd = open("/dev/null", O_RDWR);
3313965be93Schristos         if (fd == 0)
3323965be93Schristos                 fd = open("/dev/null", O_RDWR);
3333965be93Schristos         if (fd == 1)
3343965be93Schristos                 fd = open("/dev/null", O_RDWR);
3353965be93Schristos         if (fd == 2)
3363965be93Schristos                 log_perror = 0; /* No sense logging to /dev/null. */
3373965be93Schristos         else if (fd != -1)
3383965be93Schristos                 close(fd);
3393965be93Schristos 
3403965be93Schristos 	/* Parse arguments changing daemon */
3413965be93Schristos 	for (i = 1; i < argc; i++) {
3423965be93Schristos 		if (!strcmp (argv [i], "-f")) {
3433965be93Schristos #ifndef DEBUG
3443965be93Schristos 			daemon = 0;
3453965be93Schristos #endif
3463965be93Schristos 		} else if (!strcmp (argv [i], "-d")) {
3473965be93Schristos #ifndef DEBUG
3483965be93Schristos 			daemon = 0;
3493965be93Schristos #endif
3503965be93Schristos 		} else if (!strcmp (argv [i], "-t")) {
3513965be93Schristos #ifndef DEBUG
3523965be93Schristos 			daemon = 0;
3533965be93Schristos #endif
3543965be93Schristos 		} else if (!strcmp (argv [i], "-T")) {
3553965be93Schristos #ifndef DEBUG
3563965be93Schristos 			daemon = 0;
3573965be93Schristos #endif
3583965be93Schristos 		} else if (!strcmp (argv [i], "--version")) {
3593965be93Schristos 			const char vstring[] = "isc-dhcpd-";
3603965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, vstring,
3613965be93Schristos 					 strlen(vstring)));
3623965be93Schristos 			IGNORE_RET(write(STDERR_FILENO,
3633965be93Schristos 					 PACKAGE_VERSION,
3643965be93Schristos 					 strlen(PACKAGE_VERSION)));
3653965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, "\n", 1));
3663965be93Schristos 			exit (0);
3673965be93Schristos 		} else if (!strcmp(argv[i], "--help") ||
3683965be93Schristos 			   !strcmp(argv[i], "-h")) {
3693965be93Schristos 			const char *pname = isc_file_basename(progname);
3703965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, "Usage: ", 7));
3713965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname)));
3723965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, " ", 1));
3733965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGE0,
3743965be93Schristos 					 strlen(DHCPD_USAGE0)));
3753965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGE1,
3763965be93Schristos 					 strlen(DHCPD_USAGE1)));
3773965be93Schristos #if defined (PARANOIA)
3783965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEP,
3793965be93Schristos 					 strlen(DHCPD_USAGEP)));
3803965be93Schristos #endif
3813965be93Schristos #if defined (TRACING)
3823965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGET,
3833965be93Schristos 					 strlen(DHCPD_USAGET)));
3843965be93Schristos #endif
3853965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEC,
3863965be93Schristos 					 strlen(DHCPD_USAGEC)));
3873965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, "\n", 1));
3883965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, "       ", 7));
3893965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname)));
3903965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, " ", 1));
3913965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEH,
3923965be93Schristos 					 strlen(DHCPD_USAGEH)));
3933965be93Schristos 			IGNORE_RET(write(STDERR_FILENO, "\n", 1));
3943965be93Schristos 			exit(0);
3953965be93Schristos #ifdef TRACING
3963965be93Schristos 		} else if (!strcmp (argv [i], "-play")) {
3973965be93Schristos #ifndef DEBUG
3983965be93Schristos 			daemon = 0;
3993965be93Schristos #endif
4003965be93Schristos #endif
4013965be93Schristos 		}
4023965be93Schristos 	}
4033965be93Schristos 
4043965be93Schristos #ifndef DEBUG
4053965be93Schristos 	/* When not forbidden prepare to become a daemon */
4063965be93Schristos 	if (daemon) {
4073965be93Schristos 		if (pipe(dfd) == -1)
4083965be93Schristos 			log_fatal("Can't get pipe: %m");
4093965be93Schristos 		if ((pid = fork ()) < 0)
4103965be93Schristos 			log_fatal("Can't fork daemon: %m");
4113965be93Schristos 		if (pid != 0) {
4123965be93Schristos 			/* Parent: wait for the child to start */
4133965be93Schristos 			int n;
4143965be93Schristos 
4153965be93Schristos 			(void) close(dfd[1]);
4163965be93Schristos 			do {
4173965be93Schristos 				char buf;
4183965be93Schristos 
4193965be93Schristos 				n = read(dfd[0], &buf, 1);
4203965be93Schristos 				if (n == 1)
4213965be93Schristos 					_exit((int)buf);
4223965be93Schristos 			} while (n == -1 && errno == EINTR);
4233965be93Schristos 			_exit(1);
4243965be93Schristos 		}
4253965be93Schristos 		/* Child */
4263965be93Schristos 		(void) close(dfd[0]);
4273965be93Schristos 	}
4283965be93Schristos #endif
4293965be93Schristos 
4303965be93Schristos 	/* Set up the isc and dns library managers */
4313965be93Schristos 	status = dhcp_context_create(DHCP_CONTEXT_PRE_DB,
4323965be93Schristos 				     NULL, NULL);
4333965be93Schristos 	if (status != ISC_R_SUCCESS)
4343965be93Schristos 		log_fatal("Can't initialize context: %s",
4353965be93Schristos 			  isc_result_totext(status));
4363965be93Schristos 
4373965be93Schristos 	/* Set up the client classification system. */
4383965be93Schristos 	classification_setup ();
4393965be93Schristos 
4403965be93Schristos 	/* Initialize the omapi system. */
4413965be93Schristos 	result = omapi_init ();
4423965be93Schristos 	if (result != ISC_R_SUCCESS)
4433965be93Schristos 		log_fatal ("Can't initialize OMAPI: %s",
4443965be93Schristos 			   isc_result_totext (result));
4453965be93Schristos 
4463965be93Schristos 	/* Set up the OMAPI wrappers for common objects. */
4473965be93Schristos 	dhcp_db_objects_setup ();
4483965be93Schristos 	/* Set up the OMAPI wrappers for various server database internal
4493965be93Schristos 	   objects. */
4503965be93Schristos 	dhcp_common_objects_setup ();
4513965be93Schristos 
4523965be93Schristos 	/* Initially, log errors to stderr as well as to syslogd. */
4533965be93Schristos 	openlog (isc_file_basename(progname),
4543965be93Schristos 		 DHCP_LOG_OPTIONS, DHCPD_LOG_FACILITY);
4553965be93Schristos 
4563965be93Schristos 	for (i = 1; i < argc; i++) {
4573965be93Schristos 		if (!strcmp (argv [i], "-p")) {
4583965be93Schristos 			if (++i == argc)
4593965be93Schristos 				usage(use_noarg, argv[i-1]);
4603965be93Schristos 			local_port = validate_port (argv [i]);
4613965be93Schristos 			log_debug ("binding to user-specified port %d",
4623965be93Schristos 			       ntohs (local_port));
4633965be93Schristos 		} else if (!strcmp (argv [i], "-f")) {
4643965be93Schristos #ifndef DEBUG
4653965be93Schristos 			/* daemon = 0; */
4663965be93Schristos #endif
4673965be93Schristos 		} else if (!strcmp (argv [i], "-d")) {
4683965be93Schristos #ifndef DEBUG
4693965be93Schristos 			/* daemon = 0; */
4703965be93Schristos #endif
4713965be93Schristos 			log_perror = -1;
4723965be93Schristos 		} else if (!strcmp (argv [i], "-s")) {
4733965be93Schristos 			if (++i == argc)
4743965be93Schristos 				usage(use_noarg, argv[i-1]);
4753965be93Schristos 			server = argv [i];
4763965be93Schristos #if defined (PARANOIA)
4773965be93Schristos 		} else if (!strcmp (argv [i], "-user")) {
4783965be93Schristos 			if (++i == argc)
4793965be93Schristos 				usage(use_noarg, argv[i-1]);
4803965be93Schristos 			set_user = argv [i];
4813965be93Schristos 		} else if (!strcmp (argv [i], "-group")) {
4823965be93Schristos 			if (++i == argc)
4833965be93Schristos 				usage(use_noarg, argv[i-1]);
4843965be93Schristos 			set_group = argv [i];
4853965be93Schristos 		} else if (!strcmp (argv [i], "-chroot")) {
4863965be93Schristos 			if (++i == argc)
4873965be93Schristos 				usage(use_noarg, argv[i-1]);
4883965be93Schristos 			set_chroot = argv [i];
4893965be93Schristos #endif /* PARANOIA */
4903965be93Schristos 		} else if (!strcmp (argv [i], "-cf")) {
4913965be93Schristos 			if (++i == argc)
4923965be93Schristos 				usage(use_noarg, argv[i-1]);
4933965be93Schristos 			path_dhcpd_conf = argv [i];
4943965be93Schristos 			have_dhcpd_conf = 1;
4953965be93Schristos 		} else if (!strcmp (argv [i], "-lf")) {
4963965be93Schristos 			if (++i == argc)
4973965be93Schristos 				usage(use_noarg, argv[i-1]);
4983965be93Schristos 			path_dhcpd_db = argv [i];
4993965be93Schristos 			have_dhcpd_db = 1;
5003965be93Schristos 		} else if (!strcmp (argv [i], "-pf")) {
5013965be93Schristos 			if (++i == argc)
5023965be93Schristos 				usage(use_noarg, argv[i-1]);
5033965be93Schristos 			path_dhcpd_pid = argv [i];
5043965be93Schristos 			have_dhcpd_pid = 1;
5053965be93Schristos 		} else if (!strcmp(argv[i], "--no-pid")) {
5063965be93Schristos 			no_pid_file = ISC_TRUE;
5073965be93Schristos                 } else if (!strcmp (argv [i], "-t")) {
5083965be93Schristos 			/* test configurations only */
5093965be93Schristos #ifndef DEBUG
5103965be93Schristos 			/* daemon = 0; */
5113965be93Schristos #endif
5123965be93Schristos 			cftest = 1;
5133965be93Schristos 			log_perror = -1;
5143965be93Schristos                 } else if (!strcmp (argv [i], "-T")) {
5153965be93Schristos 			/* test configurations and lease file only */
5163965be93Schristos #ifndef DEBUG
5173965be93Schristos 			/* daemon = 0; */
5183965be93Schristos #endif
5193965be93Schristos 			cftest = 1;
5203965be93Schristos 			lftest = 1;
5213965be93Schristos 			log_perror = -1;
5223965be93Schristos 		} else if (!strcmp (argv [i], "-q")) {
5233965be93Schristos 			quiet = 1;
5243965be93Schristos 			quiet_interface_discovery = 1;
5253965be93Schristos #ifdef DHCPv6
5263965be93Schristos 		} else if (!strcmp(argv[i], "-4")) {
5273965be93Schristos 			if (local_family_set && (local_family != AF_INET)) {
5283965be93Schristos 				log_fatal("Server cannot run in both IPv4 and "
5293965be93Schristos 					  "IPv6 mode at the same time.");
5303965be93Schristos 			}
5313965be93Schristos 			local_family = AF_INET;
5323965be93Schristos 			local_family_set = 1;
5333965be93Schristos 		} else if (!strcmp(argv[i], "-6")) {
5343965be93Schristos 			if (local_family_set && (local_family != AF_INET6)) {
5353965be93Schristos 				log_fatal("Server cannot run in both IPv4 and "
5363965be93Schristos 					  "IPv6 mode at the same time.");
5373965be93Schristos 			}
5383965be93Schristos 			local_family = AF_INET6;
5393965be93Schristos 			local_family_set = 1;
5403965be93Schristos #ifdef DHCP4o6
5413965be93Schristos 		} else if (!strcmp(argv[i], "-4o6")) {
5423965be93Schristos 			if (++i == argc)
5433965be93Schristos 				usage(use_noarg, argv[i-1]);
5443965be93Schristos 			dhcp4o6_port = validate_port_pair(argv[i]);
5453965be93Schristos 
5463965be93Schristos 			log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d",
5473965be93Schristos 				  ntohs(dhcp4o6_port),
5483965be93Schristos 				  ntohs(dhcp4o6_port) + 1);
5493965be93Schristos 			dhcpv4_over_dhcpv6 = 1;
5503965be93Schristos #endif /* DHCP4o6 */
5513965be93Schristos #endif /* DHCPv6 */
5523965be93Schristos #if defined (TRACING)
5533965be93Schristos 		} else if (!strcmp (argv [i], "-tf")) {
5543965be93Schristos 			if (++i == argc)
5553965be93Schristos 				usage(use_noarg, argv[i-1]);
5563965be93Schristos 			traceoutfile = argv [i];
5573965be93Schristos 		} else if (!strcmp (argv [i], "-play")) {
5583965be93Schristos 			if (++i == argc)
5593965be93Schristos 				usage(use_noarg, argv[i-1]);
5603965be93Schristos 			traceinfile = argv [i];
5613965be93Schristos 			trace_replay_init ();
5623965be93Schristos #endif /* TRACING */
5633965be93Schristos 		} else if (argv [i][0] == '-') {
5643965be93Schristos 			usage("Unknown command %s", argv[i]);
5653965be93Schristos 		} else {
5663965be93Schristos 			struct interface_info *tmp =
5673965be93Schristos 				(struct interface_info *)0;
5683965be93Schristos 			if (strlen(argv[i]) >= sizeof(tmp->name))
5693965be93Schristos 				log_fatal("%s: interface name too long "
5703965be93Schristos 					  "(is %ld)",
5713965be93Schristos 					  argv[i], (long)strlen(argv[i]));
5723965be93Schristos 			result = interface_allocate (&tmp, MDL);
5733965be93Schristos 			if (result != ISC_R_SUCCESS)
5743965be93Schristos 				log_fatal ("Insufficient memory to %s %s: %s",
5753965be93Schristos 					   "record interface", argv [i],
5763965be93Schristos 					   isc_result_totext (result));
5773965be93Schristos 			strcpy (tmp -> name, argv [i]);
5783965be93Schristos 			if (interfaces) {
5793965be93Schristos 				interface_reference (&tmp -> next,
5803965be93Schristos 						     interfaces, MDL);
5813965be93Schristos 				interface_dereference (&interfaces, MDL);
5823965be93Schristos 			}
5833965be93Schristos 			interface_reference (&interfaces, tmp, MDL);
5843965be93Schristos 			tmp -> flags = INTERFACE_REQUESTED;
5853965be93Schristos 		}
5863965be93Schristos 	}
5873965be93Schristos 
5883965be93Schristos #if defined(DHCPv6) && defined(DHCP4o6)
5893965be93Schristos 	if (dhcpv4_over_dhcpv6) {
5903965be93Schristos 		if (!local_family_set)
5913965be93Schristos 			log_error("please specify the address family "
5923965be93Schristos 				  "with DHPv4 over DHCPv6 [-4|-6].");
5933965be93Schristos 		if ((local_family == AF_INET) && (interfaces != NULL))
5943965be93Schristos 			log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
5953965be93Schristos 				  "mode with command line specified "
5963965be93Schristos 				  "interfaces.");
5973965be93Schristos 	}
5983965be93Schristos #endif /* DHCPv6 && DHCP4o6 */
5993965be93Schristos 
6003965be93Schristos 	if (!have_dhcpd_conf && (s = getenv ("PATH_DHCPD_CONF"))) {
6013965be93Schristos 		path_dhcpd_conf = s;
6023965be93Schristos 	}
6033965be93Schristos 
6043965be93Schristos #ifdef DHCPv6
6053965be93Schristos         if (local_family == AF_INET6) {
6063965be93Schristos                 /* DHCPv6: override DHCPv4 lease and pid filenames */
6073965be93Schristos 	        if (!have_dhcpd_db) {
6083965be93Schristos                         if ((s = getenv ("PATH_DHCPD6_DB")))
6093965be93Schristos 		                path_dhcpd_db = s;
6103965be93Schristos                         else
6113965be93Schristos 		                path_dhcpd_db = _PATH_DHCPD6_DB;
6123965be93Schristos 	        }
6133965be93Schristos 	        if (!have_dhcpd_pid) {
6143965be93Schristos                         if ((s = getenv ("PATH_DHCPD6_PID")))
6153965be93Schristos 		                path_dhcpd_pid = s;
6163965be93Schristos                         else
6173965be93Schristos 		                path_dhcpd_pid = _PATH_DHCPD6_PID;
6183965be93Schristos 	        }
6193965be93Schristos         } else
6203965be93Schristos #endif /* DHCPv6 */
6213965be93Schristos         {
6223965be93Schristos 	        if (!have_dhcpd_db && (s = getenv ("PATH_DHCPD_DB"))) {
6233965be93Schristos 		        path_dhcpd_db = s;
6243965be93Schristos 			have_dhcpd_db = 1;
6253965be93Schristos 	        }
6263965be93Schristos 	        if (!have_dhcpd_pid && (s = getenv ("PATH_DHCPD_PID"))) {
6273965be93Schristos 		        path_dhcpd_pid = s;
6283965be93Schristos 			have_dhcpd_pid = 1;
6293965be93Schristos 	        }
6303965be93Schristos         }
6313965be93Schristos 
6323965be93Schristos         /*
6333965be93Schristos          * convert relative path names to absolute, for files that need
6343965be93Schristos          * to be reopened after chdir() has been called
6353965be93Schristos          */
6363965be93Schristos         if (have_dhcpd_db && path_dhcpd_db[0] != '/') {
6373965be93Schristos                 path_dhcpd_db = absolute_path(path_dhcpd_db);
6383965be93Schristos         }
6393965be93Schristos 
6403965be93Schristos 	if (!quiet) {
6413965be93Schristos 		log_info("%s %s", message, PACKAGE_VERSION);
6423965be93Schristos 		log_info (copyright);
6433965be93Schristos 		log_info (arr);
6443965be93Schristos 		log_info (url);
6453965be93Schristos 	} else {
6463965be93Schristos 		log_perror = 0;
6473965be93Schristos 	}
6483965be93Schristos 
64907146da4Schristos #ifndef DEBUG
65007146da4Schristos 	/*
65107146da4Schristos 	 * We need to fork before we call the context create
65207146da4Schristos 	 * call that creates the worker threads!
65307146da4Schristos 	 */
65407146da4Schristos 	if (daemon) {
65507146da4Schristos 		/* First part of becoming a daemon... */
65607146da4Schristos 		if ((pid = fork ()) < 0)
65707146da4Schristos 			log_fatal ("Can't fork daemon: %m");
65807146da4Schristos 		else if (pid)
65907146da4Schristos 			exit (0);
66007146da4Schristos 	}
66107146da4Schristos #endif
66207146da4Schristos 
66307146da4Schristos 	/* Set up the isc and dns library managers */
66407146da4Schristos 	status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL);
66507146da4Schristos 	if (status != ISC_R_SUCCESS)
66607146da4Schristos 		log_fatal("Can't initialize context: %s",
66707146da4Schristos 		          isc_result_totext(status));
66807146da4Schristos 
66907146da4Schristos 	/* Set up the client classification system. */
67007146da4Schristos 	classification_setup ();
67107146da4Schristos 
6723965be93Schristos #if defined (TRACING)
6733965be93Schristos 	trace_init (set_time, MDL);
6743965be93Schristos 	if (traceoutfile) {
6753965be93Schristos 		result = trace_begin (traceoutfile, MDL);
6763965be93Schristos 		if (result != ISC_R_SUCCESS)
6773965be93Schristos 			log_fatal ("Unable to begin trace: %s",
6783965be93Schristos 				isc_result_totext (result));
6793965be93Schristos 	}
6803965be93Schristos 	interface_trace_setup ();
6813965be93Schristos 	parse_trace_setup ();
6823965be93Schristos 	trace_srandom = trace_type_register ("random-seed", (void *)0,
6833965be93Schristos 					     trace_seed_input,
6843965be93Schristos 					     trace_seed_stop, MDL);
6853965be93Schristos #if defined (NSUPDATE)
6863965be93Schristos 	trace_ddns_init();
6873965be93Schristos #endif /* NSUPDATE */
6883965be93Schristos #endif
6893965be93Schristos 
6903965be93Schristos #if defined (PARANOIA)
6913965be93Schristos 	/* get user and group info if those options were given */
6923965be93Schristos 	if (set_user) {
6933965be93Schristos 		struct passwd *tmp_pwd;
6943965be93Schristos 
6953965be93Schristos 		if (geteuid())
6963965be93Schristos 			log_fatal ("you must be root to set user");
6973965be93Schristos 
6983965be93Schristos 		if (!(tmp_pwd = getpwnam(set_user)))
6993965be93Schristos 			log_fatal ("no such user: %s", set_user);
7003965be93Schristos 
7013965be93Schristos 		set_uid = tmp_pwd->pw_uid;
7023965be93Schristos 
7033965be93Schristos 		/* use the user's group as the default gid */
7043965be93Schristos 		if (!set_group)
7053965be93Schristos 			set_gid = tmp_pwd->pw_gid;
7063965be93Schristos 	}
7073965be93Schristos 
7083965be93Schristos 	if (set_group) {
7093965be93Schristos /* get around the ISC declaration of group */
7103965be93Schristos #define group real_group
7113965be93Schristos 		struct group *tmp_grp;
7123965be93Schristos 
7133965be93Schristos 		if (geteuid())
7143965be93Schristos 			log_fatal ("you must be root to set group");
7153965be93Schristos 
7163965be93Schristos 		if (!(tmp_grp = getgrnam(set_group)))
7173965be93Schristos 			log_fatal ("no such group: %s", set_group);
7183965be93Schristos 
7193965be93Schristos 		set_gid = tmp_grp->gr_gid;
7203965be93Schristos #undef group
7213965be93Schristos 	}
7223965be93Schristos 
7233965be93Schristos #  if defined (EARLY_CHROOT)
7243965be93Schristos 	if (set_chroot) setup_chroot (set_chroot);
7253965be93Schristos #  endif /* EARLY_CHROOT */
7263965be93Schristos #endif /* PARANOIA */
7273965be93Schristos 
7283965be93Schristos 	/* Default to the DHCP/BOOTP port. */
7293965be93Schristos 	if (!local_port)
7303965be93Schristos 	{
7313965be93Schristos 		if ((s = getenv ("DHCPD_PORT"))) {
7323965be93Schristos 			local_port = validate_port (s);
7333965be93Schristos 			log_debug ("binding to environment-specified port %d",
7343965be93Schristos 				   ntohs (local_port));
7353965be93Schristos 		} else {
7363965be93Schristos 			if (local_family == AF_INET) {
7373965be93Schristos 				ent = getservbyname("dhcp", "udp");
7383965be93Schristos 				if (ent == NULL) {
7393965be93Schristos 					local_port = htons(67);
7403965be93Schristos 				} else {
7413965be93Schristos 					local_port = ent->s_port;
7423965be93Schristos 				}
7433965be93Schristos 			} else {
7443965be93Schristos 				/* INSIST(local_family == AF_INET6); */
7453965be93Schristos 				ent = getservbyname("dhcpv6-server", "udp");
7463965be93Schristos 				if (ent == NULL) {
7473965be93Schristos 					local_port = htons(547);
7483965be93Schristos 				} else {
7493965be93Schristos 					local_port = ent->s_port;
7503965be93Schristos 				}
7513965be93Schristos 			}
7523965be93Schristos #ifndef __CYGWIN32__ /* XXX */
7533965be93Schristos 			endservent ();
7543965be93Schristos #endif
7553965be93Schristos 		}
7563965be93Schristos 	}
7573965be93Schristos 
7583965be93Schristos   	if (local_family == AF_INET) {
7593965be93Schristos 		remote_port = htons(ntohs(local_port) + 1);
7603965be93Schristos 	} else {
7613965be93Schristos 		/* INSIST(local_family == AF_INET6); */
7623965be93Schristos 		ent = getservbyname("dhcpv6-client", "udp");
7633965be93Schristos 		if (ent == NULL) {
7643965be93Schristos 			remote_port = htons(546);
7653965be93Schristos 		} else {
7663965be93Schristos 			remote_port = ent->s_port;
7673965be93Schristos 		}
7683965be93Schristos 	}
7693965be93Schristos 
7703965be93Schristos 	if (server) {
7713965be93Schristos 		if (local_family != AF_INET) {
7723965be93Schristos 			log_fatal("You can only specify address to send "
7733965be93Schristos 			          "replies to when running an IPv4 server.");
7743965be93Schristos 		}
7753965be93Schristos 		if (!inet_aton (server, &limited_broadcast)) {
7763965be93Schristos 			struct hostent *he;
7773965be93Schristos 			he = gethostbyname (server);
7783965be93Schristos 			if (he) {
7793965be93Schristos 				memcpy (&limited_broadcast,
7803965be93Schristos 					he -> h_addr_list [0],
7813965be93Schristos 					sizeof limited_broadcast);
7823965be93Schristos 			} else
7833965be93Schristos 				limited_broadcast.s_addr = INADDR_BROADCAST;
7843965be93Schristos 		}
7853965be93Schristos 	} else {
7863965be93Schristos 		limited_broadcast.s_addr = INADDR_BROADCAST;
7873965be93Schristos 	}
7883965be93Schristos 
7893965be93Schristos 	/* Get the current time... */
7903965be93Schristos 	gettimeofday(&cur_tv, NULL);
7913965be93Schristos 
7923965be93Schristos 	/* Set up the initial dhcp option universe. */
7933965be93Schristos 	initialize_common_option_spaces ();
7943965be93Schristos 	initialize_server_option_spaces ();
7953965be93Schristos 
7963965be93Schristos 	/* Add the ddns update style enumeration prior to parsing. */
7973965be93Schristos 	add_enumeration (&ddns_styles);
7983965be93Schristos 	add_enumeration (&syslog_enum);
7993965be93Schristos #if defined (LDAP_CONFIGURATION)
8003965be93Schristos 	add_enumeration (&ldap_methods);
8013965be93Schristos #if defined (LDAP_USE_SSL)
8023965be93Schristos 	add_enumeration (&ldap_ssl_usage_enum);
8033965be93Schristos 	add_enumeration (&ldap_tls_reqcert_enum);
8043965be93Schristos 	add_enumeration (&ldap_tls_crlcheck_enum);
8053965be93Schristos #endif
8063965be93Schristos #endif
8073965be93Schristos 
8083965be93Schristos 	if (!group_allocate (&root_group, MDL))
8093965be93Schristos 		log_fatal ("Can't allocate root group!");
8103965be93Schristos 	root_group -> authoritative = 0;
8113965be93Schristos 
8123965be93Schristos 	/* Set up various hooks. */
8133965be93Schristos 	dhcp_interface_setup_hook = dhcpd_interface_setup_hook;
8143965be93Schristos 	bootp_packet_handler = do_packet;
8153965be93Schristos #ifdef DHCPv6
8163965be93Schristos 	add_enumeration (&prefix_length_modes);
8173965be93Schristos 	dhcpv6_packet_handler = do_packet6;
8183965be93Schristos #endif /* DHCPv6 */
8193965be93Schristos 
8203965be93Schristos #if defined (NSUPDATE)
8213965be93Schristos 	/* Set up the standard name service updater routine. */
8223965be93Schristos 	parse = NULL;
8233965be93Schristos 	status = new_parse(&parse, -1, std_nsupdate, sizeof(std_nsupdate) - 1,
8243965be93Schristos 			    "standard name service update routine", 0);
8253965be93Schristos 	if (status != ISC_R_SUCCESS)
8263965be93Schristos 		log_fatal ("can't begin parsing name service updater!");
8273965be93Schristos 
8283965be93Schristos 	if (parse != NULL) {
8293965be93Schristos 		lose = 0;
8303965be93Schristos 		if (!(parse_executable_statements(&root_group->statements,
8313965be93Schristos 						  parse, &lose, context_any))) {
8323965be93Schristos 			end_parse(&parse);
8333965be93Schristos 			log_fatal("can't parse standard name service updater!");
8343965be93Schristos 		}
8353965be93Schristos 		end_parse(&parse);
8363965be93Schristos 	}
8373965be93Schristos #endif
8383965be93Schristos 
8393965be93Schristos 	/* Initialize icmp support... */
8403965be93Schristos 	if (!cftest && !lftest)
8413965be93Schristos 		icmp_startup (1, lease_pinged);
8423965be93Schristos 
8433965be93Schristos #if defined (TRACING)
8443965be93Schristos 	if (traceinfile) {
8453965be93Schristos 	    if (!have_dhcpd_db) {
8463965be93Schristos 		    log_error ("%s", "");
8473965be93Schristos 		    log_error ("** You must specify a lease file with -lf.");
8483965be93Schristos 		    log_error ("   Dhcpd will not overwrite your default");
8493965be93Schristos 		    log_fatal ("   lease file when playing back a trace. **");
8503965be93Schristos 	    }
8513965be93Schristos 	    trace_file_replay (traceinfile);
8523965be93Schristos 
8533965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE) && \
8543965be93Schristos                 defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
8553965be93Schristos             free_everything ();
8563965be93Schristos             omapi_print_dmalloc_usage_by_caller ();
8573965be93Schristos #endif
8583965be93Schristos 
8593965be93Schristos 	    exit (0);
8603965be93Schristos 	}
8613965be93Schristos #endif
8623965be93Schristos 
8633965be93Schristos #ifdef DHCPv6
8643965be93Schristos 	/* set up DHCPv6 hashes */
8653965be93Schristos 	if (!ia_new_hash(&ia_na_active, DEFAULT_HASH_SIZE, MDL)) {
8663965be93Schristos 		log_fatal("Out of memory creating hash for active IA_NA.");
8673965be93Schristos 	}
8683965be93Schristos 	if (!ia_new_hash(&ia_ta_active, DEFAULT_HASH_SIZE, MDL)) {
8693965be93Schristos 		log_fatal("Out of memory creating hash for active IA_TA.");
8703965be93Schristos 	}
8713965be93Schristos 	if (!ia_new_hash(&ia_pd_active, DEFAULT_HASH_SIZE, MDL)) {
8723965be93Schristos 		log_fatal("Out of memory creating hash for active IA_PD.");
8733965be93Schristos 	}
8743965be93Schristos #endif /* DHCPv6 */
8753965be93Schristos 
8763965be93Schristos 	/* Read the dhcpd.conf file... */
8773965be93Schristos 	if (readconf () != ISC_R_SUCCESS)
8783965be93Schristos 		log_fatal ("Configuration file errors encountered -- exiting");
8793965be93Schristos 
8803965be93Schristos 	postconf_initialization (quiet);
8813965be93Schristos 
8823965be93Schristos #if defined (FAILOVER_PROTOCOL)
8833965be93Schristos 	dhcp_failover_sanity_check();
8843965be93Schristos #endif
8853965be93Schristos 
8863965be93Schristos #if defined(DHCPv6) && defined(DHCP4o6)
8873965be93Schristos 	if (dhcpv4_over_dhcpv6) {
8883965be93Schristos 		if ((local_family == AF_INET) && (interfaces != NULL))
8893965be93Schristos 			log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
8903965be93Schristos 				  "mode with config file specified "
8913965be93Schristos 				  "interfaces.");
8923965be93Schristos 	}
8933965be93Schristos #endif /* DHCPv6 && DHCP4o6 */
8943965be93Schristos 
8953965be93Schristos #if defined (PARANOIA) && !defined (EARLY_CHROOT)
8963965be93Schristos 	if (set_chroot) setup_chroot (set_chroot);
8973965be93Schristos #endif /* PARANOIA && !EARLY_CHROOT */
8983965be93Schristos 
8993965be93Schristos #ifdef DHCPv6
9003965be93Schristos 	/* log info about ipv6_ponds with large address ranges */
9013965be93Schristos 	report_jumbo_ranges();
9023965be93Schristos #endif
9033965be93Schristos 
9043965be93Schristos         /* test option should cause an early exit */
9053965be93Schristos 	if (cftest && !lftest) {
9063965be93Schristos  		exit(0);
9073965be93Schristos 	}
9083965be93Schristos 
9093965be93Schristos 	/*
9103965be93Schristos 	 * First part of dealing with pid files.  Check to see if
9113965be93Schristos 	 * we should continue running or not.  We run if:
9123965be93Schristos 	 * - we are testing the lease file out
9133965be93Schristos 	 * - we don't have a pid file to check
9143965be93Schristos 	 * - there is no other process running
9153965be93Schristos 	 */
9163965be93Schristos 	if ((lftest == 0) && (no_pid_file == ISC_FALSE)) {
9173965be93Schristos 		/*Read previous pid file. */
9183965be93Schristos 		if ((i = open(path_dhcpd_pid, O_RDONLY)) >= 0) {
9193965be93Schristos 			status = read(i, pbuf, (sizeof pbuf) - 1);
9203965be93Schristos 			close(i);
9213965be93Schristos 			if (status > 0) {
9223965be93Schristos 				pbuf[status] = 0;
9233965be93Schristos 				pid = atoi(pbuf);
9243965be93Schristos 
9253965be93Schristos 				/*
9263965be93Schristos 				 * If there was a previous server process and
9273965be93Schristos 				 * it is still running, abort
9283965be93Schristos 				 */
9293965be93Schristos 				if (!pid ||
9303965be93Schristos 				    (pid != getpid() && kill(pid, 0) == 0))
9313965be93Schristos 					log_fatal("There's already a "
9323965be93Schristos 						  "DHCP server running.");
9333965be93Schristos 			}
9343965be93Schristos 		}
9353965be93Schristos 	}
9363965be93Schristos 
9373965be93Schristos 	group_write_hook = group_writer;
9383965be93Schristos 
9393965be93Schristos 	/* Start up the database... */
9403965be93Schristos 	db_startup (lftest);
9413965be93Schristos 
9423965be93Schristos 	if (lftest)
9433965be93Schristos 		exit (0);
9443965be93Schristos 
9453965be93Schristos 	/* Discover all the network interfaces and initialize them. */
9463965be93Schristos #if defined(DHCPv6) && defined(DHCP4o6)
9473965be93Schristos 	if (dhcpv4_over_dhcpv6) {
9483965be93Schristos 		int real_family = local_family;
9493965be93Schristos 		local_family = AF_INET6;
9503965be93Schristos 		/* The DHCPv4 side of DHCPv4-over-DHCPv6 service
9513965be93Schristos 		   uses a specific discovery which doesn't register
9523965be93Schristos 		   DHCPv6 sockets. */
9533965be93Schristos 		if (real_family == AF_INET)
9543965be93Schristos 			discover_interfaces(DISCOVER_SERVER46);
9553965be93Schristos 		else
9563965be93Schristos 			discover_interfaces(DISCOVER_SERVER);
9573965be93Schristos 		local_family = real_family;
9583965be93Schristos 	} else
9593965be93Schristos #endif /* DHCPv6 && DHCP4o6 */
9603965be93Schristos 	discover_interfaces(DISCOVER_SERVER);
9613965be93Schristos 
9623965be93Schristos #ifdef DHCPv6
9633965be93Schristos 	/*
9643965be93Schristos 	 * Remove addresses from our pools that we should not issue
9653965be93Schristos 	 * to clients.
9663965be93Schristos 	 *
9673965be93Schristos 	 * We currently have no support for this in IPv4. It is not
9683965be93Schristos 	 * as important in IPv4, as making pools with ranges that
9693965be93Schristos 	 * leave out interfaces and hosts is fairly straightforward
9703965be93Schristos 	 * using range notation, but not so handy with CIDR notation.
9713965be93Schristos 	 */
9723965be93Schristos 	if (local_family == AF_INET6) {
9733965be93Schristos 		mark_hosts_unavailable();
9743965be93Schristos 		mark_phosts_unavailable();
9753965be93Schristos 		mark_interfaces_unavailable();
9763965be93Schristos 	}
9773965be93Schristos #endif /* DHCPv6 */
9783965be93Schristos 
9793965be93Schristos 	/* Make up a seed for the random number generator from current
9803965be93Schristos 	   time plus the sum of the last four bytes of each
9813965be93Schristos 	   interface's hardware address interpreted as an integer.
9823965be93Schristos 	   Not much entropy, but we're booting, so we're not likely to
9833965be93Schristos 	   find anything better. */
9843965be93Schristos 	seed = 0;
9853965be93Schristos 	for (ip = interfaces; ip; ip = ip -> next) {
9863965be93Schristos 		int junk;
9873965be93Schristos 		memcpy (&junk,
9883965be93Schristos 			&ip -> hw_address.hbuf [ip -> hw_address.hlen -
9893965be93Schristos 					       sizeof seed], sizeof seed);
9903965be93Schristos 		seed += junk;
9913965be93Schristos 	}
9923965be93Schristos 	srandom (seed + cur_time);
9933965be93Schristos #if defined (TRACING)
9943965be93Schristos 	trace_seed_stash (trace_srandom, seed + cur_time);
9953965be93Schristos #endif
9963965be93Schristos 	postdb_startup ();
9973965be93Schristos 
9983965be93Schristos #ifdef DHCPv6
9993965be93Schristos 	/*
10003965be93Schristos 	 * Set server DHCPv6 identifier - we go in order:
10013965be93Schristos 	 * dhcp6.server-id in the config file
10023965be93Schristos 	 * server-duid from the lease file
10033965be93Schristos 	 * server-duid from the config file (the config file is read first
10043965be93Schristos 	 * and the lease file overwrites the config file information)
10053965be93Schristos 	 * generate a new one from the interface hardware addresses.
10063965be93Schristos 	 * In all cases we write it out to the lease file.
10073965be93Schristos 	 * See dhcpv6.c for discussion of setting DUID.
10083965be93Schristos 	 */
10093965be93Schristos 	if ((set_server_duid_from_option() != ISC_R_SUCCESS) &&
10103965be93Schristos 	    (!server_duid_isset()) &&
10113965be93Schristos 	    (generate_new_server_duid() != ISC_R_SUCCESS)) {
10123965be93Schristos 		log_fatal("Unable to set server identifier.");
10133965be93Schristos 	}
10143965be93Schristos 	write_server_duid();
10153965be93Schristos #ifdef DHCP4o6
10163965be93Schristos 	if (dhcpv4_over_dhcpv6)
10173965be93Schristos 		dhcp4o6_setup(dhcp4o6_port);
10183965be93Schristos #endif /* DHCP4o6 */
10193965be93Schristos #endif /* DHCPv6 */
10203965be93Schristos 
10213965be93Schristos #ifndef DEBUG
10223965be93Schristos 	/*
10233965be93Schristos 	 * Second part of dealing with pid files.  Now
10243965be93Schristos 	 * that we have forked we can write our pid if
10253965be93Schristos 	 * appropriate.
10263965be93Schristos 	 */
10273965be93Schristos 	if (no_pid_file == ISC_FALSE) {
10283965be93Schristos 		i = open(path_dhcpd_pid, O_WRONLY|O_CREAT|O_TRUNC, 0644);
10293965be93Schristos 		if (i >= 0) {
10303965be93Schristos 			sprintf(pbuf, "%d\n", (int) getpid());
10313965be93Schristos 			IGNORE_RET(write(i, pbuf, strlen(pbuf)));
10323965be93Schristos 			close(i);
10333965be93Schristos 		} else {
10343965be93Schristos 			log_error("Can't create PID file %s: %m.",
10353965be93Schristos 				  path_dhcpd_pid);
10363965be93Schristos 		}
10373965be93Schristos 	}
10383965be93Schristos 
10393965be93Schristos #if defined (PARANOIA)
10403965be93Schristos 	/* change uid to the specified one */
10413965be93Schristos 
10423965be93Schristos 	if (set_gid) {
10433965be93Schristos 		if (setgroups (0, (void *)0))
10443965be93Schristos 			log_fatal ("setgroups: %m");
10453965be93Schristos 		if (setgid (set_gid))
10463965be93Schristos 			log_fatal ("setgid(%d): %m", (int) set_gid);
10473965be93Schristos 	}
10483965be93Schristos 
10493965be93Schristos 	if (set_uid) {
10503965be93Schristos 		if (setuid (set_uid))
10513965be93Schristos 			log_fatal ("setuid(%d): %m", (int) set_uid);
10523965be93Schristos 	}
10533965be93Schristos #endif /* PARANOIA */
10543965be93Schristos 
10553965be93Schristos 	/* If we were requested to log to stdout on the command line,
10563965be93Schristos 	   keep doing so; otherwise, stop. */
10573965be93Schristos 	if (log_perror == -1)
10583965be93Schristos 		log_perror = 1;
10593965be93Schristos 	else
10603965be93Schristos 		log_perror = 0;
10613965be93Schristos 
10623965be93Schristos 	if (daemon) {
10633965be93Schristos 		if (dfd[0] != -1 && dfd[1] != -1) {
10643965be93Schristos 			char buf = 0;
10653965be93Schristos 
10663965be93Schristos 			if (write(dfd[1], &buf, 1) != 1)
10673965be93Schristos 				log_fatal("write to parent: %m");
10683965be93Schristos 			(void) close(dfd[1]);
10693965be93Schristos 			dfd[0] = dfd[1] = -1;
10703965be93Schristos 		}
10713965be93Schristos 
10723965be93Schristos 		/* Become session leader and get pid... */
10733965be93Schristos 		(void) setsid();
10743965be93Schristos 
10753965be93Schristos                 /* Close standard I/O descriptors. */
10763965be93Schristos                 (void) close(0);
10773965be93Schristos                 (void) close(1);
10783965be93Schristos                 (void) close(2);
10793965be93Schristos 
10803965be93Schristos                 /* Reopen them on /dev/null. */
10813965be93Schristos                 (void) open("/dev/null", O_RDWR);
10823965be93Schristos                 (void) open("/dev/null", O_RDWR);
10833965be93Schristos                 (void) open("/dev/null", O_RDWR);
10843965be93Schristos                 log_perror = 0; /* No sense logging to /dev/null. */
10853965be93Schristos 
10863965be93Schristos        		IGNORE_RET (chdir("/"));
10873965be93Schristos 	}
10883965be93Schristos #endif /* !DEBUG */
10893965be93Schristos 
10903965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
10913965be93Schristos 		defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
10923965be93Schristos 	dmalloc_cutoff_generation = dmalloc_generation;
10933965be93Schristos 	dmalloc_longterm = dmalloc_outstanding;
10943965be93Schristos 	dmalloc_outstanding = 0;
10953965be93Schristos #endif
10963965be93Schristos 
10973965be93Schristos 	omapi_set_int_value ((omapi_object_t *)dhcp_control_object,
10983965be93Schristos 			     (omapi_object_t *)0, "state", server_running);
10993965be93Schristos 
11003965be93Schristos #if defined(ENABLE_GENTLE_SHUTDOWN)
11013965be93Schristos 	/* no signal handlers until we deal with the side effects */
11023965be93Schristos         /* install signal handlers */
11033965be93Schristos 	signal(SIGINT, dhcp_signal_handler);   /* control-c */
11043965be93Schristos 	signal(SIGTERM, dhcp_signal_handler);  /* kill */
11053965be93Schristos #endif
11063965be93Schristos 
11073965be93Schristos 	/* Log that we are about to start working */
11083965be93Schristos 	log_info("Server starting service.");
11093965be93Schristos 
11103965be93Schristos 	/*
11113965be93Schristos 	 * Receive packets and dispatch them...
11123965be93Schristos 	 * dispatch() will never return.
11133965be93Schristos 	 */
11143965be93Schristos 	dispatch ();
11153965be93Schristos 
11163965be93Schristos 	/* Let's return status code */
11173965be93Schristos 	return 0;
11183965be93Schristos }
11193965be93Schristos #endif /* !UNIT_TEST */
11203965be93Schristos 
postconf_initialization(int quiet)11213965be93Schristos void postconf_initialization (int quiet)
11223965be93Schristos {
11233965be93Schristos 	struct option_state *options = NULL;
11243965be93Schristos 	struct data_string db;
11253965be93Schristos 	struct option_cache *oc;
11263965be93Schristos 	char *s;
11273965be93Schristos 	isc_result_t result;
11283965be93Schristos 	int tmp;
11293965be93Schristos #if defined (NSUPDATE)
11303965be93Schristos 	struct in_addr  local4, *local4_ptr = NULL;
11313965be93Schristos 	struct in6_addr local6, *local6_ptr = NULL;
11323965be93Schristos #endif
11333965be93Schristos 
11343965be93Schristos 	/* Now try to get the lease file name. */
11353965be93Schristos 	option_state_allocate(&options, MDL);
11363965be93Schristos 
11373965be93Schristos 	execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL,
11383965be93Schristos 				    options, &global_scope, root_group,
11393965be93Schristos 				    NULL, NULL);
11403965be93Schristos 	memset(&db, 0, sizeof db);
11413965be93Schristos 	oc = lookup_option(&server_universe, options, SV_LEASE_FILE_NAME);
11423965be93Schristos 	if (oc &&
11433965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
11443965be93Schristos 				  &global_scope, oc, MDL)) {
11453965be93Schristos 		s = dmalloc(db.len + 1, MDL);
11463965be93Schristos 		if (!s)
11473965be93Schristos 			log_fatal("no memory for lease db filename.");
11483965be93Schristos 		memcpy(s, db.data, db.len);
11493965be93Schristos 		s[db.len] = 0;
11503965be93Schristos 		data_string_forget(&db, MDL);
11513965be93Schristos 		path_dhcpd_db = s;
11523965be93Schristos 	}
11533965be93Schristos 
11543965be93Schristos 	oc = lookup_option(&server_universe, options, SV_PID_FILE_NAME);
11553965be93Schristos 	if (oc &&
11563965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
11573965be93Schristos 				  &global_scope, oc, MDL)) {
11583965be93Schristos 		s = dmalloc(db.len + 1, MDL);
11593965be93Schristos 		if (!s)
11603965be93Schristos 			log_fatal("no memory for pid filename.");
11613965be93Schristos 		memcpy(s, db.data, db.len);
11623965be93Schristos 		s[db.len] = 0;
11633965be93Schristos 		data_string_forget(&db, MDL);
11643965be93Schristos 		path_dhcpd_pid = s;
11653965be93Schristos 	}
11663965be93Schristos 
11673965be93Schristos #ifdef DHCPv6
11683965be93Schristos         if (local_family == AF_INET6) {
11693965be93Schristos                 /*
11703965be93Schristos                  * Override lease file name with dhcpv6 lease file name,
11713965be93Schristos                  * if it was set; then, do the same with the pid file name
11723965be93Schristos                  */
11733965be93Schristos                 oc = lookup_option(&server_universe, options,
11743965be93Schristos                                    SV_DHCPV6_LEASE_FILE_NAME);
11753965be93Schristos                 if (oc &&
11763965be93Schristos                     evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
11773965be93Schristos 					  &global_scope, oc, MDL)) {
11783965be93Schristos                         s = dmalloc(db.len + 1, MDL);
11793965be93Schristos                         if (!s)
11803965be93Schristos                                 log_fatal("no memory for lease db filename.");
11813965be93Schristos                         memcpy(s, db.data, db.len);
11823965be93Schristos                         s[db.len] = 0;
11833965be93Schristos                         data_string_forget(&db, MDL);
11843965be93Schristos                         path_dhcpd_db = s;
11853965be93Schristos                 }
11863965be93Schristos 
11873965be93Schristos                 oc = lookup_option(&server_universe, options,
11883965be93Schristos                                    SV_DHCPV6_PID_FILE_NAME);
11893965be93Schristos                 if (oc &&
11903965be93Schristos                     evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
11913965be93Schristos 					  &global_scope, oc, MDL)) {
11923965be93Schristos                         s = dmalloc(db.len + 1, MDL);
11933965be93Schristos                         if (!s)
11943965be93Schristos                                 log_fatal("no memory for pid filename.");
11953965be93Schristos                         memcpy(s, db.data, db.len);
11963965be93Schristos                         s[db.len] = 0;
11973965be93Schristos                         data_string_forget(&db, MDL);
11983965be93Schristos                         path_dhcpd_pid = s;
11993965be93Schristos                 }
12003965be93Schristos 
12013965be93Schristos 		oc = lookup_option(&server_universe, options,
12023965be93Schristos 				   SV_LOCAL_ADDRESS6);
12033965be93Schristos 		if (oc &&
12043965be93Schristos 		    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12053965be93Schristos 					  &global_scope, oc, MDL)) {
12063965be93Schristos 			if (db.len == 16) {
12073965be93Schristos 				memcpy(&local_address6, db.data, 16);
12083965be93Schristos 			} else
12093965be93Schristos 				log_fatal("invalid local address "
12103965be93Schristos 					  "data length");
12113965be93Schristos 			data_string_forget(&db, MDL);
12123965be93Schristos 		}
12133965be93Schristos 
12143965be93Schristos 		oc = lookup_option(&server_universe, options,
12153965be93Schristos 				   SV_BIND_LOCAL_ADDRESS6);
12163965be93Schristos 		if (oc &&
12173965be93Schristos 		    evaluate_boolean_option_cache(NULL, NULL, NULL,
12183965be93Schristos 						  NULL, options, NULL,
12193965be93Schristos 						  &global_scope, oc, MDL)) {
12203965be93Schristos 			bind_local_address6 = 1;
12213965be93Schristos 		}
12223965be93Schristos 
12233965be93Schristos         }
12243965be93Schristos #endif /* DHCPv6 */
12253965be93Schristos 
12263965be93Schristos 	omapi_port = -1;
12273965be93Schristos 	oc = lookup_option(&server_universe, options, SV_OMAPI_PORT);
12283965be93Schristos 	if (oc &&
12293965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12303965be93Schristos 				  &global_scope, oc, MDL)) {
12313965be93Schristos 		if (db.len == 2) {
12323965be93Schristos 			omapi_port = getUShort(db.data);
12333965be93Schristos 		} else
12343965be93Schristos 			log_fatal("invalid omapi port data length");
12353965be93Schristos 		data_string_forget(&db, MDL);
12363965be93Schristos 	}
12373965be93Schristos 
12383965be93Schristos 	oc = lookup_option(&server_universe, options, SV_OMAPI_KEY);
12393965be93Schristos 	if (oc &&
12403965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12413965be93Schristos 				  &global_scope, oc, MDL)) {
12423965be93Schristos 		s = dmalloc(db.len + 1, MDL);
12433965be93Schristos 		if (!s)
12443965be93Schristos 			log_fatal("no memory for OMAPI key filename.");
12453965be93Schristos 		memcpy(s, db.data, db.len);
12463965be93Schristos 		s[db.len] = 0;
12473965be93Schristos 		data_string_forget(&db, MDL);
12483965be93Schristos 		result = omapi_auth_key_lookup_name(&omapi_key, s);
12493965be93Schristos 		dfree(s, MDL);
12503965be93Schristos 		if (result != ISC_R_SUCCESS)
12513965be93Schristos 			log_fatal("OMAPI key %s: %s",
12523965be93Schristos 				  s, isc_result_totext (result));
12533965be93Schristos 	}
12543965be93Schristos 
12553965be93Schristos 	oc = lookup_option(&server_universe, options, SV_LOCAL_PORT);
12563965be93Schristos 	if (oc &&
12573965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12583965be93Schristos 				  &global_scope, oc, MDL)) {
12593965be93Schristos 		if (db.len == 2) {
12603965be93Schristos 			local_port = htons(getUShort (db.data));
12613965be93Schristos 		} else
12623965be93Schristos 			log_fatal("invalid local port data length");
12633965be93Schristos 		data_string_forget(&db, MDL);
12643965be93Schristos 	}
12653965be93Schristos 
12663965be93Schristos 	oc = lookup_option(&server_universe, options, SV_REMOTE_PORT);
12673965be93Schristos 	if (oc &&
12683965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12693965be93Schristos 				  &global_scope, oc, MDL)) {
12703965be93Schristos 		if (db.len == 2) {
12713965be93Schristos 			remote_port = htons(getUShort (db.data));
12723965be93Schristos 		} else
12733965be93Schristos 			log_fatal("invalid remote port data length");
12743965be93Schristos 		data_string_forget(&db, MDL);
12753965be93Schristos 	}
12763965be93Schristos 
12773965be93Schristos 	oc = lookup_option(&server_universe, options,
12783965be93Schristos 			   SV_LIMITED_BROADCAST_ADDRESS);
12793965be93Schristos 	if (oc &&
12803965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12813965be93Schristos 				  &global_scope, oc, MDL)) {
12823965be93Schristos 		if (db.len == 4) {
12833965be93Schristos 			memcpy(&limited_broadcast, db.data, 4);
12843965be93Schristos 		} else
12853965be93Schristos 			log_fatal("invalid broadcast address data length");
12863965be93Schristos 		data_string_forget(&db, MDL);
12873965be93Schristos 	}
12883965be93Schristos 
12893965be93Schristos 	oc = lookup_option(&server_universe, options, SV_LOCAL_ADDRESS);
12903965be93Schristos 	if (oc &&
12913965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
12923965be93Schristos 				  &global_scope, oc, MDL)) {
12933965be93Schristos 		if (db.len == 4) {
12943965be93Schristos 			memcpy(&local_address, db.data, 4);
12953965be93Schristos 		} else
12963965be93Schristos 			log_fatal("invalid local address data length");
12973965be93Schristos 		data_string_forget(&db, MDL);
12983965be93Schristos 	}
12993965be93Schristos 
13003965be93Schristos 	oc = lookup_option(&server_universe, options, SV_DDNS_UPDATE_STYLE);
13013965be93Schristos 	if (oc) {
13023965be93Schristos 		if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
13033965be93Schristos 					  &global_scope, oc, MDL)) {
13043965be93Schristos 			if (db.len == 1) {
13053965be93Schristos 				ddns_update_style = db.data[0];
13063965be93Schristos 			} else
13073965be93Schristos 				log_fatal("invalid dns update type");
13083965be93Schristos 			data_string_forget(&db, MDL);
13093965be93Schristos 		}
13103965be93Schristos 	} else {
13113965be93Schristos 		ddns_update_style = DDNS_UPDATE_STYLE_NONE;
13123965be93Schristos 	}
13133965be93Schristos #if defined (NSUPDATE)
13143965be93Schristos 	/* We no longer support ad_hoc, tell the user */
13153965be93Schristos 	if (ddns_update_style == DDNS_UPDATE_STYLE_AD_HOC) {
13163965be93Schristos 		log_fatal("ddns-update-style ad_hoc no longer supported");
13173965be93Schristos 	}
13183965be93Schristos 
13193965be93Schristos 	oc = lookup_option(&server_universe, options, SV_DDNS_LOCAL_ADDRESS4);
13203965be93Schristos 	if (oc) {
13213965be93Schristos 		if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
13223965be93Schristos 					  &global_scope, oc, MDL)) {
13233965be93Schristos 			if (db.len == 4) {
13243965be93Schristos 				memcpy(&local4, db.data, 4);
13253965be93Schristos 				local4_ptr = &local4;
13263965be93Schristos 			}
13273965be93Schristos 			data_string_forget(&db, MDL);
13283965be93Schristos 		}
13293965be93Schristos 	}
13303965be93Schristos 
13313965be93Schristos 	oc = lookup_option(&server_universe, options, SV_DDNS_LOCAL_ADDRESS6);
13323965be93Schristos 	if (oc) {
13333965be93Schristos 		if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
13343965be93Schristos 					  &global_scope, oc, MDL)) {
13353965be93Schristos 			if (db.len == 16) {
13363965be93Schristos 				memcpy(&local6, db.data, 16);
13373965be93Schristos 				local6_ptr = &local6;
13383965be93Schristos 			}
13393965be93Schristos 			data_string_forget(&db, MDL);
13403965be93Schristos 		}
13413965be93Schristos 	}
13423965be93Schristos 
13433965be93Schristos 	/* Don't init DNS client if update style is none. This avoids
13443965be93Schristos 	 * listening ports that aren't needed.  We don't use ddns-udpates
13453965be93Schristos 	 * as that has multiple levels of scope. */
13463965be93Schristos 	if (ddns_update_style != DDNS_UPDATE_STYLE_NONE) {
13473965be93Schristos 		if (dhcp_context_create(DHCP_CONTEXT_POST_DB,
13483965be93Schristos 					local4_ptr, local6_ptr)
13493965be93Schristos 			!= ISC_R_SUCCESS) {
13503965be93Schristos 			log_fatal("Unable to complete ddns initialization");
13513965be93Schristos 		}
13523965be93Schristos 	}
13533965be93Schristos 
13543965be93Schristos 	/* Set the conflict detection flag mask based on globally
13553965be93Schristos 	 * defined DDNS configuration params.  This mask should be
13563965be93Schristos 	 * to init ddns_cb::flags before for every DDNS transaction. */
13573965be93Schristos 	ddns_conflict_mask = get_conflict_mask(options);
13583965be93Schristos 
13593965be93Schristos #else
13603965be93Schristos 	/* If we don't have support for updates compiled in tell the user */
13613965be93Schristos 	if (ddns_update_style != DDNS_UPDATE_STYLE_NONE) {
13623965be93Schristos 		log_fatal("Support for ddns-update-style not compiled in");
13633965be93Schristos 	}
13643965be93Schristos #endif
13653965be93Schristos 
13663965be93Schristos 	if (!quiet) {
13673965be93Schristos 		log_info ("Config file: %s", path_dhcpd_conf);
13683965be93Schristos 		log_info ("Database file: %s", path_dhcpd_db);
13693965be93Schristos 		log_info ("PID file: %s", path_dhcpd_pid);
13703965be93Schristos 	}
13713965be93Schristos 
13723965be93Schristos 	oc = lookup_option(&server_universe, options, SV_LOG_FACILITY);
13733965be93Schristos 	if (oc) {
13743965be93Schristos 		if (evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
13753965be93Schristos 					  &global_scope, oc, MDL)) {
13763965be93Schristos 			if (db.len == 1) {
13773965be93Schristos 				closelog ();
13783965be93Schristos 				openlog(isc_file_basename(progname),
13793965be93Schristos 					DHCP_LOG_OPTIONS, db.data[0]);
13803965be93Schristos 				/* Log the startup banner into the new
13813965be93Schristos 				   log file. */
13823965be93Schristos 				/* Don't log to stderr twice. */
13833965be93Schristos 				tmp = log_perror;
13843965be93Schristos 				log_perror = 0;
13853965be93Schristos 				log_info("%s %s", message, PACKAGE_VERSION);
13863965be93Schristos 				log_info(copyright);
13873965be93Schristos 				log_info(arr);
13883965be93Schristos 				log_info(url);
13893965be93Schristos 				log_perror = tmp;
13903965be93Schristos 			} else
13913965be93Schristos 				log_fatal("invalid log facility");
13923965be93Schristos 			data_string_forget(&db, MDL);
13933965be93Schristos 		}
13943965be93Schristos 	}
13953965be93Schristos 
13963965be93Schristos #if defined(DELAYED_ACK)
13973965be93Schristos 	oc = lookup_option(&server_universe, options, SV_DELAYED_ACK);
13983965be93Schristos 	if (oc &&
13993965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
14003965be93Schristos 				  &global_scope, oc, MDL)) {
14013965be93Schristos 		if (db.len == 2) {
14023965be93Schristos 			max_outstanding_acks = htons(getUShort(db.data));
14033965be93Schristos 		} else {
14043965be93Schristos 			log_fatal("invalid max delayed ACK count ");
14053965be93Schristos 		}
14063965be93Schristos 		data_string_forget(&db, MDL);
14073965be93Schristos 	}
14083965be93Schristos #if defined(DHCP4o6)
14093965be93Schristos 	/* Delayed acks and DHCPv4-over-DHCPv6 are incompatible */
14103965be93Schristos 	if (dhcpv4_over_dhcpv6) {
14113965be93Schristos 		if (max_outstanding_acks > 0) {
14123965be93Schristos 			log_debug("DHCP4o6 enabled, "
14133965be93Schristos 				  "setting delayed-ack to zero (incompatible)");
14143965be93Schristos 		}
14153965be93Schristos 
14163965be93Schristos 		max_outstanding_acks = 0;
14173965be93Schristos 	}
14183965be93Schristos #endif
14193965be93Schristos 
14203965be93Schristos 	oc = lookup_option(&server_universe, options, SV_MAX_ACK_DELAY);
14213965be93Schristos 	if (oc &&
14223965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
14233965be93Schristos 				  &global_scope, oc, MDL)) {
14243965be93Schristos 		u_int32_t timeval;
14253965be93Schristos 
14263965be93Schristos 		if (db.len != 4)
14273965be93Schristos 			log_fatal("invalid max ack delay configuration");
14283965be93Schristos 
14293965be93Schristos 		timeval = getULong(db.data);
14303965be93Schristos 		max_ack_delay_secs  = timeval / 1000000;
14313965be93Schristos 		max_ack_delay_usecs = timeval % 1000000;
14323965be93Schristos 
14333965be93Schristos 		data_string_forget(&db, MDL);
14343965be93Schristos 	}
14353965be93Schristos #endif
14363965be93Schristos 
14373965be93Schristos 	oc = lookup_option(&server_universe, options, SV_DONT_USE_FSYNC);
14383965be93Schristos 	if ((oc != NULL) &&
14393965be93Schristos 	    evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL,
14403965be93Schristos 					  &global_scope, oc, MDL)) {
14413965be93Schristos 		dont_use_fsync = 1;
14423965be93Schristos 		log_error("Not using fsync() to flush lease writes");
14433965be93Schristos 	}
14443965be93Schristos 
14453965be93Schristos        oc = lookup_option(&server_universe, options, SV_SERVER_ID_CHECK);
14463965be93Schristos        if ((oc != NULL) &&
14473965be93Schristos 	   evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL,
14483965be93Schristos 					 &global_scope, oc, MDL)) {
14493965be93Schristos 		log_info("Setting server-id-check true");
14503965be93Schristos 		server_id_check = 1;
14513965be93Schristos 	}
14523965be93Schristos 
14533965be93Schristos #ifdef DHCPv6
14543965be93Schristos 	oc = lookup_option(&server_universe, options, SV_PREFIX_LEN_MODE);
14553965be93Schristos 	if ((oc != NULL) &&
14563965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
14573965be93Schristos 					  &global_scope, oc, MDL)) {
14583965be93Schristos 		if (db.len == 1) {
14593965be93Schristos 			prefix_length_mode = db.data[0];
14603965be93Schristos 		} else {
14613965be93Schristos 			log_fatal("invalid prefix-len-mode");
14623965be93Schristos 		}
14633965be93Schristos 
14643965be93Schristos 		data_string_forget(&db, MDL);
14653965be93Schristos 	}
14663965be93Schristos #endif
14673965be93Schristos 
14683965be93Schristos 	// Set global abandon-lease-time option.
14693965be93Schristos 	oc = lookup_option (&server_universe, options, SV_ABANDON_LEASE_TIME);
14703965be93Schristos 	if ((oc != NULL) &&
14713965be93Schristos 	    evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
14723965be93Schristos 				  &global_scope, oc, MDL)) {
14733965be93Schristos 		if (db.len == sizeof (u_int32_t)) {
14743965be93Schristos 			abandon_lease_time = getULong (db.data);
14753965be93Schristos 		} else {
14763965be93Schristos 			log_fatal("invalid abandon-lease-time");
14773965be93Schristos 		}
14783965be93Schristos 
14793965be93Schristos 		data_string_forget (&db, MDL);
14803965be93Schristos         }
14813965be93Schristos 
14823965be93Schristos #if defined (FAILOVER_PROTOCOL)
14833965be93Schristos        oc = lookup_option(&server_universe, options, SV_CHECK_SECS_BYTE_ORDER);
14843965be93Schristos        if ((oc != NULL) &&
14853965be93Schristos 	   evaluate_boolean_option_cache(NULL, NULL, NULL, NULL, options, NULL,
14863965be93Schristos 					 &global_scope, oc, MDL)) {
14873965be93Schristos 		check_secs_byte_order = 1;
14883965be93Schristos 	}
14893965be93Schristos #endif
14903965be93Schristos 
14913965be93Schristos #ifdef EUI_64
14923965be93Schristos        oc = lookup_option(&server_universe, options, SV_PERSIST_EUI_64_LEASES);
14933965be93Schristos        if (oc != NULL) {
14943965be93Schristos 		persist_eui64 = evaluate_boolean_option_cache(NULL, NULL, NULL,
14953965be93Schristos 							      NULL, options,
14963965be93Schristos 							      NULL,
14973965be93Schristos 							      &global_scope,
14983965be93Schristos 							      oc, MDL);
14993965be93Schristos 	}
15003965be93Schristos 
15013965be93Schristos 	if (!persist_eui64) {
15023965be93Schristos 		log_info("EUI64 leases will not be written to lease file");
15033965be93Schristos 	}
15043965be93Schristos #endif
15053965be93Schristos 
15063965be93Schristos #ifdef DHCPv6
15073965be93Schristos 	oc = lookup_option(&server_universe, options, SV_RELEASE_ON_ROAM);
15083965be93Schristos 	if (oc != NULL) {
15093965be93Schristos 		do_release_on_roam =
15103965be93Schristos 			evaluate_boolean_option_cache(NULL, NULL, NULL, NULL,
15113965be93Schristos 						      options, NULL,
15123965be93Schristos 						      &global_scope, oc, MDL);
15133965be93Schristos 	}
15143965be93Schristos #endif
15153965be93Schristos 
15163965be93Schristos #if defined (BINARY_LEASES)
15173965be93Schristos 	if (local_family == AF_INET) {
15183965be93Schristos 		log_info("Source compiled to use binary-leases");
15193965be93Schristos 	}
15203965be93Schristos #endif
15213965be93Schristos 
15223965be93Schristos 	/* Don't need the options anymore. */
15233965be93Schristos 	option_state_dereference(&options, MDL);
15243965be93Schristos }
15253965be93Schristos 
postdb_startup(void)15263965be93Schristos void postdb_startup (void)
15273965be93Schristos {
15283965be93Schristos 	/* Initialize the omapi listener state. */
15293965be93Schristos 	if (omapi_port != -1) {
15303965be93Schristos 		omapi_listener_start (0);
15313965be93Schristos 	}
15323965be93Schristos 
15333965be93Schristos #if defined (FAILOVER_PROTOCOL)
15343965be93Schristos 	/* Initialize the failover listener state. */
15353965be93Schristos 	dhcp_failover_startup ();
15363965be93Schristos #endif
15373965be93Schristos 
15383965be93Schristos 	/*
15393965be93Schristos 	 * Begin our lease timeout background task.
15403965be93Schristos 	 */
15413965be93Schristos 	schedule_all_ipv6_lease_timeouts();
15423965be93Schristos }
15433965be93Schristos 
lease_pinged(from,packet,length)15443965be93Schristos void lease_pinged (from, packet, length)
15453965be93Schristos 	struct iaddr from;
15463965be93Schristos 	u_int8_t *packet;
15473965be93Schristos 	int length;
15483965be93Schristos {
15493965be93Schristos 	struct lease *lp;
15503965be93Schristos 
15513965be93Schristos 	/* Don't try to look up a pinged lease if we aren't trying to
15523965be93Schristos 	   ping one - otherwise somebody could easily make us churn by
15533965be93Schristos 	   just forging repeated ICMP EchoReply packets for us to look
15543965be93Schristos 	   up. */
15553965be93Schristos 	if (!outstanding_pings)
15563965be93Schristos 		return;
15573965be93Schristos 
15583965be93Schristos 	lp = (struct lease *)0;
15593965be93Schristos 	if (!find_lease_by_ip_addr (&lp, from, MDL)) {
15603965be93Schristos 		log_debug ("unexpected ICMP Echo Reply from %s",
15613965be93Schristos 			   piaddr (from));
15623965be93Schristos 		return;
15633965be93Schristos 	}
15643965be93Schristos 
15653965be93Schristos 	if (!lp -> state) {
15663965be93Schristos #if defined (FAILOVER_PROTOCOL)
15673965be93Schristos 		if (!lp -> pool ||
15683965be93Schristos 		    !lp -> pool -> failover_peer)
15693965be93Schristos #endif
15703965be93Schristos 			log_debug ("ICMP Echo Reply for %s late or spurious.",
15713965be93Schristos 				   piaddr (from));
15723965be93Schristos 		goto out;
15733965be93Schristos 	}
15743965be93Schristos 
15753965be93Schristos 	if (lp -> ends > cur_time) {
15763965be93Schristos 		log_debug ("ICMP Echo reply while lease %s valid.",
15773965be93Schristos 			   piaddr (from));
15783965be93Schristos 	}
15793965be93Schristos 
15803965be93Schristos 	/* At this point it looks like we pinged a lease and got a
15813965be93Schristos 	   response, which shouldn't have happened. */
15823965be93Schristos 	data_string_forget (&lp -> state -> parameter_request_list, MDL);
15833965be93Schristos 	free_lease_state (lp -> state, MDL);
15843965be93Schristos 	lp -> state = (struct lease_state *)0;
15853965be93Schristos 
15863965be93Schristos 	abandon_lease (lp, "pinged before offer");
15873965be93Schristos 	cancel_timeout (lease_ping_timeout, lp);
15883965be93Schristos 	--outstanding_pings;
15893965be93Schristos       out:
15903965be93Schristos 	lease_dereference (&lp, MDL);
15913965be93Schristos }
15923965be93Schristos 
lease_ping_timeout(vlp)15933965be93Schristos void lease_ping_timeout (vlp)
15943965be93Schristos 	void *vlp;
15953965be93Schristos {
15963965be93Schristos 	struct lease *lp = vlp;
15973965be93Schristos 
15983965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE)
15993965be93Schristos 	unsigned long previous_outstanding = dmalloc_outstanding;
16003965be93Schristos #endif
16013965be93Schristos 
16023965be93Schristos 	--outstanding_pings;
16033965be93Schristos 	dhcp_reply (lp);
16043965be93Schristos 
16053965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE)
16063965be93Schristos 	log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
16073965be93Schristos 		  dmalloc_generation,
16083965be93Schristos 		  dmalloc_outstanding - previous_outstanding,
16093965be93Schristos 		  dmalloc_outstanding, dmalloc_longterm);
16103965be93Schristos #endif
16113965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE)
16123965be93Schristos 	dmalloc_dump_outstanding ();
16133965be93Schristos #endif
16143965be93Schristos }
16153965be93Schristos 
dhcpd_interface_setup_hook(struct interface_info * ip,struct iaddr * ia)16163965be93Schristos int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia)
16173965be93Schristos {
16183965be93Schristos 	struct subnet *subnet;
16193965be93Schristos 	struct shared_network *share;
16203965be93Schristos 	isc_result_t status;
16213965be93Schristos 
16223965be93Schristos 	/* Special case for fallback network - not sure why this is
16233965be93Schristos 	   necessary. */
16243965be93Schristos 	if (!ia) {
16253965be93Schristos 		const char *fnn = "fallback-net";
16263965be93Schristos 		status = shared_network_allocate (&ip -> shared_network, MDL);
16273965be93Schristos 		if (status != ISC_R_SUCCESS)
16283965be93Schristos 			log_fatal ("No memory for shared subnet: %s",
16293965be93Schristos 				   isc_result_totext (status));
16303965be93Schristos 		ip -> shared_network -> name = dmalloc (strlen (fnn) + 1, MDL);
16313965be93Schristos 		if (!ip -> shared_network -> name)
16323965be93Schristos 			log_fatal("no memory for shared network");
16333965be93Schristos 		strcpy (ip -> shared_network -> name, fnn);
16343965be93Schristos 		return 1;
16353965be93Schristos 	}
16363965be93Schristos 
16373965be93Schristos 	/* If there's a registered subnet for this address,
16383965be93Schristos 	   connect it together... */
16393965be93Schristos 	subnet = (struct subnet *)0;
16403965be93Schristos 	if (find_subnet (&subnet, *ia, MDL)) {
16413965be93Schristos 		/* If this interface has multiple aliases on the same
16423965be93Schristos 		   subnet, ignore all but the first we encounter. */
16433965be93Schristos 		if (!subnet -> interface) {
16443965be93Schristos 			interface_reference (&subnet -> interface, ip, MDL);
16453965be93Schristos 			subnet -> interface_address = *ia;
16463965be93Schristos 		} else if (subnet -> interface != ip) {
16473965be93Schristos 			log_error ("Multiple interfaces match the %s: %s %s",
16483965be93Schristos 				   "same subnet",
16493965be93Schristos 				   subnet -> interface -> name, ip -> name);
16503965be93Schristos 		}
16513965be93Schristos 		share = subnet -> shared_network;
16523965be93Schristos 		if (ip -> shared_network &&
16533965be93Schristos 		    ip -> shared_network != share) {
16543965be93Schristos 			log_fatal ("Interface %s matches multiple shared %s",
16553965be93Schristos 				   ip -> name, "networks");
16563965be93Schristos 		} else {
16573965be93Schristos 			if (!ip -> shared_network)
16583965be93Schristos 				shared_network_reference
16593965be93Schristos 					(&ip -> shared_network, share, MDL);
16603965be93Schristos 		}
16613965be93Schristos 
16623965be93Schristos 		if (!share -> interface) {
16633965be93Schristos 			interface_reference (&share -> interface, ip, MDL);
16643965be93Schristos 		} else if (share -> interface != ip) {
16653965be93Schristos 			log_error ("Multiple interfaces match the %s: %s %s",
16663965be93Schristos 				   "same shared network",
16673965be93Schristos 				   share -> interface -> name, ip -> name);
16683965be93Schristos 		}
16693965be93Schristos 		subnet_dereference (&subnet, MDL);
16703965be93Schristos 	}
16713965be93Schristos 	return 1;
16723965be93Schristos }
16733965be93Schristos 
16743965be93Schristos static TIME shutdown_time;
16753965be93Schristos static int omapi_connection_count;
16763965be93Schristos enum dhcp_shutdown_state shutdown_state;
16773965be93Schristos 
dhcp_io_shutdown(omapi_object_t * obj,void * foo)16783965be93Schristos isc_result_t dhcp_io_shutdown (omapi_object_t *obj, void *foo)
16793965be93Schristos {
16803965be93Schristos 	/* Shut down all listeners. */
16813965be93Schristos 	if (shutdown_state == shutdown_listeners &&
16823965be93Schristos 	    obj -> type == omapi_type_listener &&
16833965be93Schristos 	    obj -> inner &&
16843965be93Schristos 	    obj -> inner -> type == omapi_type_protocol_listener) {
16853965be93Schristos 		omapi_listener_destroy (obj, MDL);
16863965be93Schristos 		return ISC_R_SUCCESS;
16873965be93Schristos 	}
16883965be93Schristos 
16893965be93Schristos 	/* Shut down all existing omapi connections. */
16903965be93Schristos 	if (obj -> type == omapi_type_connection &&
16913965be93Schristos 	    obj -> inner &&
16923965be93Schristos 	    obj -> inner -> type == omapi_type_protocol) {
16933965be93Schristos 		if (shutdown_state == shutdown_drop_omapi_connections) {
16943965be93Schristos 			omapi_disconnect (obj, 1);
16953965be93Schristos 		}
16963965be93Schristos 		omapi_connection_count++;
16973965be93Schristos 		if (shutdown_state == shutdown_omapi_connections) {
16983965be93Schristos 			omapi_disconnect (obj, 0);
16993965be93Schristos 			return ISC_R_SUCCESS;
17003965be93Schristos 		}
17013965be93Schristos 	}
17023965be93Schristos 
17033965be93Schristos 	/* Shutdown all DHCP interfaces. */
17043965be93Schristos 	if (obj -> type == dhcp_type_interface &&
17053965be93Schristos 	    shutdown_state == shutdown_dhcp) {
17063965be93Schristos 		dhcp_interface_remove (obj, (omapi_object_t *)0);
17073965be93Schristos 		return ISC_R_SUCCESS;
17083965be93Schristos 	}
17093965be93Schristos 	return ISC_R_SUCCESS;
17103965be93Schristos }
17113965be93Schristos 
dhcp_io_shutdown_countdown(void * vlp)17123965be93Schristos static isc_result_t dhcp_io_shutdown_countdown (void *vlp)
17133965be93Schristos {
17143965be93Schristos #if defined (FAILOVER_PROTOCOL)
17153965be93Schristos 	dhcp_failover_state_t *state;
17163965be93Schristos 	int failover_connection_count = 0;
17173965be93Schristos #endif
17183965be93Schristos 	struct timeval tv;
17193965be93Schristos 
17203965be93Schristos       oncemore:
17213965be93Schristos 	if (shutdown_state == shutdown_listeners ||
17223965be93Schristos 	    shutdown_state == shutdown_omapi_connections ||
17233965be93Schristos 	    shutdown_state == shutdown_drop_omapi_connections ||
17243965be93Schristos 	    shutdown_state == shutdown_dhcp) {
17253965be93Schristos 		omapi_connection_count = 0;
17263965be93Schristos 		omapi_io_state_foreach (dhcp_io_shutdown, 0);
17273965be93Schristos 	}
17283965be93Schristos 
17293965be93Schristos 	if ((shutdown_state == shutdown_listeners ||
17303965be93Schristos 	     shutdown_state == shutdown_omapi_connections ||
17313965be93Schristos 	     shutdown_state == shutdown_drop_omapi_connections) &&
17323965be93Schristos 	    omapi_connection_count == 0) {
17333965be93Schristos 		shutdown_state = shutdown_dhcp;
17343965be93Schristos 		shutdown_time = cur_time;
17353965be93Schristos 		goto oncemore;
17363965be93Schristos 	} else if (shutdown_state == shutdown_listeners &&
17373965be93Schristos 		   cur_time - shutdown_time > 4) {
17383965be93Schristos 		shutdown_state = shutdown_omapi_connections;
17393965be93Schristos 		shutdown_time = cur_time;
17403965be93Schristos 	} else if (shutdown_state == shutdown_omapi_connections &&
17413965be93Schristos 		   cur_time - shutdown_time > 4) {
17423965be93Schristos 		shutdown_state = shutdown_drop_omapi_connections;
17433965be93Schristos 		shutdown_time = cur_time;
17443965be93Schristos 	} else if (shutdown_state == shutdown_drop_omapi_connections &&
17453965be93Schristos 		   cur_time - shutdown_time > 4) {
17463965be93Schristos 		shutdown_state = shutdown_dhcp;
17473965be93Schristos 		shutdown_time = cur_time;
17483965be93Schristos 		goto oncemore;
17493965be93Schristos 	} else if (shutdown_state == shutdown_dhcp &&
17503965be93Schristos 		   cur_time - shutdown_time > 4) {
17513965be93Schristos 		shutdown_state = shutdown_done;
17523965be93Schristos 		shutdown_time = cur_time;
17533965be93Schristos 	}
17543965be93Schristos 
17553965be93Schristos #if defined (FAILOVER_PROTOCOL)
17563965be93Schristos 	/* Set all failover peers into the shutdown state. */
17573965be93Schristos 	if (shutdown_state == shutdown_dhcp) {
17583965be93Schristos 	    for (state = failover_states; state; state = state -> next) {
17593965be93Schristos 		if (state -> me.state == normal) {
17603965be93Schristos 		    dhcp_failover_set_state (state, shut_down);
17613965be93Schristos 		    failover_connection_count++;
17623965be93Schristos 		}
17633965be93Schristos 		if (state -> me.state == shut_down &&
17643965be93Schristos 		    state -> partner.state != partner_down)
17653965be93Schristos 			failover_connection_count++;
17663965be93Schristos 	    }
17673965be93Schristos 	}
17683965be93Schristos 
17693965be93Schristos 	if (shutdown_state == shutdown_done) {
17703965be93Schristos 	    for (state = failover_states; state; state = state -> next) {
17713965be93Schristos 		if (state -> me.state == shut_down) {
17723965be93Schristos 		    if (state -> link_to_peer)
17733965be93Schristos 			dhcp_failover_link_dereference (&state -> link_to_peer,
17743965be93Schristos 							MDL);
17753965be93Schristos 		    dhcp_failover_set_state (state, recover);
17763965be93Schristos 		}
17773965be93Schristos 	    }
17783965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE) && \
17793965be93Schristos 		defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
17803965be93Schristos 	    free_everything ();
17813965be93Schristos 	    omapi_print_dmalloc_usage_by_caller ();
17823965be93Schristos #endif
17833965be93Schristos 	    if (no_pid_file == ISC_FALSE)
17843965be93Schristos 		    (void) unlink(path_dhcpd_pid);
17853965be93Schristos 	    exit (0);
17863965be93Schristos 	}
17873965be93Schristos #else
17883965be93Schristos 	if (shutdown_state == shutdown_done) {
17893965be93Schristos #if defined (DEBUG_MEMORY_LEAKAGE) && \
17903965be93Schristos 		defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
17913965be93Schristos 		free_everything ();
17923965be93Schristos 		omapi_print_dmalloc_usage_by_caller ();
17933965be93Schristos #endif
17943965be93Schristos 		if (no_pid_file == ISC_FALSE)
17953965be93Schristos 			(void) unlink(path_dhcpd_pid);
17963965be93Schristos 		exit (0);
17973965be93Schristos 	}
17983965be93Schristos #endif
17993965be93Schristos 	if (shutdown_state == shutdown_dhcp &&
18003965be93Schristos #if defined(FAILOVER_PROTOCOL)
18013965be93Schristos 	    !failover_connection_count &&
18023965be93Schristos #endif
18033965be93Schristos 	    ISC_TRUE) {
18043965be93Schristos 		shutdown_state = shutdown_done;
18053965be93Schristos 		shutdown_time = cur_time;
18063965be93Schristos 		goto oncemore;
18073965be93Schristos 	}
18083965be93Schristos 	tv.tv_sec = cur_tv.tv_sec + 1;
18093965be93Schristos 	tv.tv_usec = cur_tv.tv_usec;
18103965be93Schristos 	add_timeout (&tv,
18113965be93Schristos 		     (void (*)(void *))dhcp_io_shutdown_countdown, 0, 0, 0);
18123965be93Schristos 	return ISC_R_SUCCESS;
18133965be93Schristos }
18143965be93Schristos 
dhcp_set_control_state(control_object_state_t oldstate,control_object_state_t newstate)18153965be93Schristos isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
18163965be93Schristos 				     control_object_state_t newstate)
18173965be93Schristos {
18183965be93Schristos 	struct timeval tv;
18193965be93Schristos 
18203965be93Schristos 	if (newstate != server_shutdown)
18213965be93Schristos 		return DHCP_R_INVALIDARG;
18223965be93Schristos 	/* Re-entry. */
18233965be93Schristos 	if (shutdown_signal == SIGUSR1)
18243965be93Schristos 		return ISC_R_SUCCESS;
18253965be93Schristos 	shutdown_time = cur_time;
18263965be93Schristos 	shutdown_state = shutdown_listeners;
18273965be93Schristos 	/* Called by user. */
18283965be93Schristos 	if (shutdown_signal == 0) {
18293965be93Schristos 		shutdown_signal = SIGUSR1;
18303965be93Schristos 		dhcp_io_shutdown_countdown (0);
18313965be93Schristos 		return ISC_R_SUCCESS;
18323965be93Schristos 	}
18333965be93Schristos 	/* Called on signal. */
18343965be93Schristos 	log_info("Received signal %d, initiating shutdown.", shutdown_signal);
18353965be93Schristos 	shutdown_signal = SIGUSR1;
18363965be93Schristos 
18373965be93Schristos 	/*
18383965be93Schristos 	 * Prompt the shutdown event onto the timer queue
18393965be93Schristos 	 * and return to the dispatch loop.
18403965be93Schristos 	 */
18413965be93Schristos 	tv.tv_sec = cur_tv.tv_sec;
18423965be93Schristos 	tv.tv_usec = cur_tv.tv_usec + 1;
18433965be93Schristos 	add_timeout(&tv,
18443965be93Schristos 		    (void (*)(void *))dhcp_io_shutdown_countdown, 0, 0, 0);
18453965be93Schristos 	return ISC_R_SUCCESS;
18463965be93Schristos }
1847