dhcpd.c 48.7 KB
Newer Older
Ted Lemon's avatar
Ted Lemon committed
1 2 3 4 5
/* dhcpd.c

   DHCP Server Daemon. */

/*
6
 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
7
 * Copyright (c) 1996-2003 by Internet Software Consortium
Ted Lemon's avatar
Ted Lemon committed
8
 *
9 10 11
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Ted Lemon's avatar
Ted Lemon committed
12
 *
13 14 15 16 17 18 19
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Ted Lemon's avatar
Ted Lemon committed
20
 *
21 22 23 24
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
25
 *   https://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
26
 *
Ted Lemon's avatar
Ted Lemon committed
27 28
 */

29
static const char copyright[] =
30
"Copyright 2004-2017 Internet Systems Consortium.";
31 32 33
static const char arr [] = "All rights reserved.";
static const char message [] = "Internet Systems Consortium DHCP Server";
static const char url [] =
34
"For info, please visit https://www.isc.org/software/dhcp/";
Ted Lemon's avatar
Ted Lemon committed
35 36

#include "dhcpd.h"
37
#include <omapip/omapip_p.h>
38
#include <syslog.h>
Shawn Routhier's avatar
Shawn Routhier committed
39
#include <signal.h>
40
#include <errno.h>
Shane Kerr's avatar
Shane Kerr committed
41
#include <limits.h>
42
#include <sys/types.h>
Francis Dupont's avatar
Francis Dupont committed
43
#include <sys/time.h>
Francis Dupont's avatar
Francis Dupont committed
44
#include <isc/file.h>
Ted Lemon's avatar
Ted Lemon committed
45

46 47 48 49 50
#if defined (PARANOIA)
#  include <sys/types.h>
#  include <unistd.h>
#  include <pwd.h>
/* get around the ISC declaration of group */
51
#  define group real_group
52 53
#    include <grp.h>
#  undef group
54 55 56 57

/* global values so db.c can look at them */
uid_t set_uid = 0;
gid_t set_gid = 0;
58 59
#endif /* PARANOIA */

60 61 62
struct class unknown_class;
struct class known_class;

63 64 65
struct iaddr server_identifier;
int server_identifier_matched;

66
#if defined (NSUPDATE)
67 68 69

/* This stuff is always executed to figure the default values for certain
   ddns variables. */
Ted Lemon's avatar
Ted Lemon committed
70
char std_nsupdate [] = "						    \n\
71
option server.ddns-hostname =						    \n\
72
  pick (option fqdn.hostname, option host-name, config-option host-name);   \n\
73
option server.ddns-domainname =	config-option domain-name;		    \n\
74 75
option server.ddns-rev-domainname = \"in-addr.arpa.\";";

76 77
/* Stores configured DDNS conflict detection flags */
u_int16_t ddns_conflict_mask;
78
#endif /* NSUPDATE */
79

80
int ddns_update_style;
81
int dont_use_fsync = 0; /* 0 = default, use fsync, 1 = don't use fsync */
82
int server_id_check = 0; /* 0 = default, don't check server id, 1 = do check */
83 84

#ifdef DHCPv6
85
int prefix_length_mode = PLM_PREFER;
86 87
int do_release_on_roam = 0; /* 0 = default, do not release v6 leases on roam */
#endif
Ted Lemon's avatar
Ted Lemon committed
88

89 90 91 92
#ifdef EUI_64
int persist_eui64 = 1; /* 1 = write EUI64 leases to disk, 0 = don't */
#endif

93
int authoring_byte_order = 0; /* 0 = not set */
94
int lease_id_format = TOKEN_OCTAL; /* octal by default */
95
u_int32_t abandon_lease_time = DEFAULT_ABANDON_LEASE_TIME;
96

97 98 99
const char *path_dhcpd_conf = _PATH_DHCPD_CONF;
const char *path_dhcpd_db = _PATH_DHCPD_DB;
const char *path_dhcpd_pid = _PATH_DHCPD_PID;
100 101
/* False (default) => we write and use a pid file */
isc_boolean_t no_pid_file = ISC_FALSE;
Ted Lemon's avatar
Ted Lemon committed
102

103 104
int dhcp_max_agent_option_packet_length = DHCP_MTU_MAX;

105
static omapi_auth_key_t *omapi_key = (omapi_auth_key_t *)0;
106 107 108 109 110
int omapi_port;

#if defined (TRACING)
trace_type_t *trace_srandom;
#endif
111

Francis Dupont's avatar
Francis Dupont committed
112 113
char *progname;

114 115 116 117 118 119
static isc_result_t verify_addr (omapi_object_t *l, omapi_addr_t *addr) {
	return ISC_R_SUCCESS;
}

static isc_result_t verify_auth (omapi_object_t *p, omapi_auth_key_t *a) {
	if (a != omapi_key)
120
		return DHCP_R_INVALIDKEY;
121 122 123
	return ISC_R_SUCCESS;
}

124 125 126 127
static void omapi_listener_start (void *foo)
{
	omapi_object_t *listener;
	isc_result_t result;
Francis Dupont's avatar
Francis Dupont committed
128
	struct timeval tv;
129 130 131 132 133 134 135 136 137 138 139 140 141 142

	listener = (omapi_object_t *)0;
	result = omapi_generic_new (&listener, MDL);
	if (result != ISC_R_SUCCESS)
		log_fatal ("Can't allocate new generic object: %s",
			   isc_result_totext (result));
	result = omapi_protocol_listen (listener,
					(unsigned)omapi_port, 1);
	if (result == ISC_R_SUCCESS && omapi_key)
		result = omapi_protocol_configure_security
			(listener, verify_addr, verify_auth);
	if (result != ISC_R_SUCCESS) {
		log_error ("Can't start OMAPI protocol: %s",
			   isc_result_totext (result));
143 144
		tv.tv_sec = cur_tv.tv_sec + 5;
		tv.tv_usec = cur_tv.tv_usec;
Francis Dupont's avatar
Francis Dupont committed
145
		add_timeout (&tv, omapi_listener_start, 0, 0, 0);
146 147 148 149
	}
	omapi_object_dereference (&listener, MDL);
}

150 151
#ifndef UNIT_TEST

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
#define DHCPD_USAGE0 \
"[-p <UDP port #>] [-f] [-d] [-q] [-t|-T]\n"

#ifdef DHCPv6
#ifdef DHCP4o6
#define DHCPD_USAGE1 \
"             [-4|-6] [-4o6 <port>]\n" \
"             [-cf config-file] [-lf lease-file]\n"
#else /* DHCP4o6 */
#define DHCPD_USAGE1 \
"             [-4|-6] [-cf config-file] [-lf lease-file]\n"
#endif /* DHCP4o6 */
#else /* !DHCPv6 */
#define DHCPD_USAGE1 \
"             [-cf config-file] [-lf lease-file]\n"
#endif /* DHCPv6 */

#if defined (PARANOIA)
#define DHCPD_USAGEP \
"             [-user user] [-group group] [-chroot dir]\n"
#else
#define DHCPD_USAGEP ""
#endif /* PARANOIA */

#if defined (TRACING)
#define DHCPD_USAGET \
"             [-tf trace-output-file]\n" \
"             [-play trace-input-file]\n"
#else
#define DHCPD_USAGET ""
#endif /* TRACING */

#define DHCPD_USAGEC \
"             [-pf pid-file] [--no-pid] [-s server]\n" \
"             [if0 [...ifN]]"

#define DHCPD_USAGEH "{--version|--help|-h}"

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
/*!
 *
 * \brief Print the generic usage message
 *
 * If the user has provided an incorrect command line print out
 * the description of the command line.  The arguments provide
 * a way for the caller to request more specific information about
 * the error be printed as well.  Mostly this will be that some
 * comamnd doesn't include its argument.
 *
 * \param sfmt - The basic string and format for the specific error
 * \param sarg - Generally the offending argument from the comamnd line.
 *
 * \return Nothing
 */
static char use_noarg[] = "No argument for command: %s ";

static void
usage(const char *sfmt, const char *sarg) {
	log_info("%s %s", message, PACKAGE_VERSION);
	log_info(copyright);
	log_info(arr);
212
	log_info(url);
213 214 215 216 217 218 219

	/* If desired print out the specific error message */
#ifdef PRINT_SPECIFIC_CL_ERRORS
	if (sfmt != NULL)
		log_error(sfmt, sarg);
#endif

220 221 222 223 224 225 226 227 228
	log_fatal("Usage: %s %s%s%s%s%s\n       %s %s",
		  isc_file_basename(progname),
		  DHCPD_USAGE0,
		  DHCPD_USAGE1,
		  DHCPD_USAGEP,
		  DHCPD_USAGET,
		  DHCPD_USAGEC,
		  isc_file_basename(progname),
		  DHCPD_USAGEH);
229 230
}

231 232 233 234
/* Note: If we add unit tests to test setup_chroot it will
 * need to be moved to be outside the ifndef UNIT_TEST block.
 */

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
#if defined (PARANOIA)
/* to be used in one of two possible scenarios */
static void setup_chroot (char *chroot_dir) {
  if (geteuid())
    log_fatal ("you must be root to use chroot");

  if (chroot(chroot_dir)) {
    log_fatal ("chroot(\"%s\"): %m", chroot_dir);
  }
  if (chdir ("/")) {
    /* probably permission denied */
    log_fatal ("chdir(\"/\"): %m");
  }
}
#endif /* PARANOIA */

251
int
David Hankins's avatar
David Hankins committed
252
main(int argc, char **argv) {
253
	int fd;
254
	int i, status;
Ted Lemon's avatar
Ted Lemon committed
255
	struct servent *ent;
256
	char *s;
Ted Lemon's avatar
Ted Lemon committed
257
	int cftest = 0;
Ted Lemon's avatar
Ted Lemon committed
258
	int lftest = 0;
Ted Lemon's avatar
Ted Lemon committed
259
	int pid;
260
	char pbuf [20];
261
#ifndef DEBUG
262
	int daemon = 1;
263
	int dfd[2] = { -1, -1 };
264
#endif
265
	int quiet = 0;
266
	char *server = (char *)0;
267
	isc_result_t result;
268
	unsigned seed;
269
	struct interface_info *ip;
270
#if defined (NSUPDATE)
Ted Lemon's avatar
Ted Lemon committed
271 272
	struct parse *parse;
	int lose;
273
#endif
274 275 276
	int no_dhcpd_conf = 0;
	int no_dhcpd_db = 0;
	int no_dhcpd_pid = 0;
277
#ifdef DHCPv6
David Hankins's avatar
David Hankins committed
278
	int local_family_set = 0;
279 280 281
#ifdef DHCP4o6
	u_int16_t dhcp4o6_port = 0;
#endif /* DHCP4o6 */
282
#endif /* DHCPv6 */
Ted Lemon's avatar
Ted Lemon committed
283
#if defined (TRACING)
284 285
	char *traceinfile = (char *)0;
	char *traceoutfile = (char *)0;
Ted Lemon's avatar
Ted Lemon committed
286
#endif
Ted Lemon's avatar
Ted Lemon committed
287

288 289 290 291 292 293
#if defined (PARANOIA)
	char *set_user   = 0;
	char *set_group  = 0;
	char *set_chroot = 0;
#endif /* PARANOIA */

Francis Dupont's avatar
Francis Dupont committed
294 295 296 297 298 299
#ifdef OLD_LOG_NAME
	progname = "dhcpd";
#else
	progname = argv[0];
#endif

300 301
        /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
           2 (stderr) are open. To do this, we assume that when we
Francis Dupont's avatar
Francis Dupont committed
302
           open a file the lowest available file descriptor is used. */
303
        fd = open("/dev/null", O_RDWR);
304
        if (fd == 0)
305
                fd = open("/dev/null", O_RDWR);
306
        if (fd == 1)
307
                fd = open("/dev/null", O_RDWR);
308 309 310
        if (fd == 2)
                log_perror = 0; /* No sense logging to /dev/null. */
        else if (fd != -1)
311
                close(fd);
312

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	/* Parse arguments changing daemon */
	for (i = 1; i < argc; i++) {
		if (!strcmp (argv [i], "-f")) {
#ifndef DEBUG
			daemon = 0;
#endif
		} else if (!strcmp (argv [i], "-d")) {
#ifndef DEBUG
			daemon = 0;
#endif
		} else if (!strcmp (argv [i], "-t")) {
#ifndef DEBUG
			daemon = 0;
#endif
		} else if (!strcmp (argv [i], "-T")) {
#ifndef DEBUG
			daemon = 0;
#endif
		} else if (!strcmp (argv [i], "--version")) {
			const char vstring[] = "isc-dhcpd-";
			IGNORE_RET(write(STDERR_FILENO, vstring,
					 strlen(vstring)));
			IGNORE_RET(write(STDERR_FILENO,
					 PACKAGE_VERSION,
					 strlen(PACKAGE_VERSION)));
			IGNORE_RET(write(STDERR_FILENO, "\n", 1));
			exit (0);
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
		} else if (!strcmp(argv[i], "--help") ||
			   !strcmp(argv[i], "-h")) {
			const char *pname = isc_file_basename(progname);
			IGNORE_RET(write(STDERR_FILENO, "Usage: ", 7));
			IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname)));
			IGNORE_RET(write(STDERR_FILENO, " ", 1));
			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGE0,
					 strlen(DHCPD_USAGE0)));
			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGE1,
					 strlen(DHCPD_USAGE1)));
#if defined (PARANOIA)
			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEP,
					 strlen(DHCPD_USAGEP)));
#endif
#if defined (TRACING)
			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGET,
					 strlen(DHCPD_USAGET)));
#endif
			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEC,
					 strlen(DHCPD_USAGEC)));
			IGNORE_RET(write(STDERR_FILENO, "\n", 1));
			IGNORE_RET(write(STDERR_FILENO, "       ", 7));
			IGNORE_RET(write(STDERR_FILENO, pname, strlen(pname)));
			IGNORE_RET(write(STDERR_FILENO, " ", 1));
			IGNORE_RET(write(STDERR_FILENO, DHCPD_USAGEH,
					 strlen(DHCPD_USAGEH)));
			IGNORE_RET(write(STDERR_FILENO, "\n", 1));
			exit(0);
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
#ifdef TRACING
		} else if (!strcmp (argv [i], "-play")) {
#ifndef DEBUG
			daemon = 0;
#endif
#endif
		}
	}

#ifndef DEBUG
	/* When not forbidden prepare to become a daemon */
	if (daemon) {
		if (pipe(dfd) == -1)
			log_fatal("Can't get pipe: %m");
		if ((pid = fork ()) < 0)
			log_fatal("Can't fork daemon: %m");
		if (pid != 0) {
			/* Parent: wait for the child to start */
			int n;

			(void) close(dfd[1]);
			do {
				char buf;

				n = read(dfd[0], &buf, 1);
				if (n == 1)
					_exit((int)buf);
			} while (n == -1 && errno == EINTR);
			_exit(1);
		}
		/* Child */
		(void) close(dfd[0]);
	}
#endif

403
	/* Set up the isc and dns library managers */
404 405
	status = dhcp_context_create(DHCP_CONTEXT_PRE_DB,
				     NULL, NULL);
406 407 408 409
	if (status != ISC_R_SUCCESS)
		log_fatal("Can't initialize context: %s",
			  isc_result_totext(status));

410 411 412 413 414 415 416 417 418
	/* Set up the client classification system. */
	classification_setup ();

	/* Initialize the omapi system. */
	result = omapi_init ();
	if (result != ISC_R_SUCCESS)
		log_fatal ("Can't initialize OMAPI: %s",
			   isc_result_totext (result));

419 420
	/* Set up the OMAPI wrappers for common objects. */
	dhcp_db_objects_setup ();
421 422 423 424
	/* Set up the OMAPI wrappers for various server database internal
	   objects. */
	dhcp_common_objects_setup ();

425
	/* Initially, log errors to stderr as well as to syslogd. */
Francis Dupont's avatar
Francis Dupont committed
426 427
	openlog (isc_file_basename(progname),
		 DHCP_LOG_OPTIONS, DHCPD_LOG_FACILITY);
428

Ted Lemon's avatar
Ted Lemon committed
429 430 431
	for (i = 1; i < argc; i++) {
		if (!strcmp (argv [i], "-p")) {
			if (++i == argc)
432
				usage(use_noarg, argv[i-1]);
433
			local_port = validate_port (argv [i]);
434
			log_debug ("binding to user-specified port %d",
435
			       ntohs (local_port));
436 437
		} else if (!strcmp (argv [i], "-f")) {
#ifndef DEBUG
438
			/* daemon = 0; */
439 440
#endif
		} else if (!strcmp (argv [i], "-d")) {
Ted Lemon's avatar
Ted Lemon committed
441
#ifndef DEBUG
442
			/* daemon = 0; */
Ted Lemon's avatar
Ted Lemon committed
443
#endif
Ted Lemon's avatar
Ted Lemon committed
444
			log_perror = -1;
445 446
		} else if (!strcmp (argv [i], "-s")) {
			if (++i == argc)
447
				usage(use_noarg, argv[i-1]);
448
			server = argv [i];
449 450 451
#if defined (PARANOIA)
		} else if (!strcmp (argv [i], "-user")) {
			if (++i == argc)
452
				usage(use_noarg, argv[i-1]);
453 454 455
			set_user = argv [i];
		} else if (!strcmp (argv [i], "-group")) {
			if (++i == argc)
456
				usage(use_noarg, argv[i-1]);
457 458 459
			set_group = argv [i];
		} else if (!strcmp (argv [i], "-chroot")) {
			if (++i == argc)
460
				usage(use_noarg, argv[i-1]);
461 462
			set_chroot = argv [i];
#endif /* PARANOIA */
463 464
		} else if (!strcmp (argv [i], "-cf")) {
			if (++i == argc)
465
				usage(use_noarg, argv[i-1]);
466
			path_dhcpd_conf = argv [i];
467
			no_dhcpd_conf = 1;
468 469
		} else if (!strcmp (argv [i], "-lf")) {
			if (++i == argc)
470
				usage(use_noarg, argv[i-1]);
471
			path_dhcpd_db = argv [i];
472
			no_dhcpd_db = 1;
473 474
		} else if (!strcmp (argv [i], "-pf")) {
			if (++i == argc)
475
				usage(use_noarg, argv[i-1]);
476
			path_dhcpd_pid = argv [i];
477
			no_dhcpd_pid = 1;
478 479
		} else if (!strcmp(argv[i], "--no-pid")) {
			no_pid_file = ISC_TRUE;
480 481 482
                } else if (!strcmp (argv [i], "-t")) {
			/* test configurations only */
#ifndef DEBUG
483
			/* daemon = 0; */
484 485 486
#endif
			cftest = 1;
			log_perror = -1;
Ted Lemon's avatar
Ted Lemon committed
487 488 489
                } else if (!strcmp (argv [i], "-T")) {
			/* test configurations and lease file only */
#ifndef DEBUG
490
			/* daemon = 0; */
Ted Lemon's avatar
Ted Lemon committed
491 492 493 494
#endif
			cftest = 1;
			lftest = 1;
			log_perror = -1;
495
		} else if (!strcmp (argv [i], "-q")) {
496 497
			quiet = 1;
			quiet_interface_discovery = 1;
498
#ifdef DHCPv6
David Hankins's avatar
David Hankins committed
499 500 501 502 503 504 505 506 507 508 509 510 511 512
		} else if (!strcmp(argv[i], "-4")) {
			if (local_family_set && (local_family != AF_INET)) {
				log_fatal("Server cannot run in both IPv4 and "
					  "IPv6 mode at the same time.");
			}
			local_family = AF_INET;
			local_family_set = 1;
		} else if (!strcmp(argv[i], "-6")) {
			if (local_family_set && (local_family != AF_INET6)) {
				log_fatal("Server cannot run in both IPv4 and "
					  "IPv6 mode at the same time.");
			}
			local_family = AF_INET6;
			local_family_set = 1;
513 514 515 516 517 518 519 520 521 522 523
#ifdef DHCP4o6
		} else if (!strcmp(argv[i], "-4o6")) {
			if (++i == argc)
				usage(use_noarg, argv[i-1]);
			dhcp4o6_port = validate_port_pair(argv[i]);

			log_debug("DHCPv4 over DHCPv6 over ::1 port %d and %d",
				  ntohs(dhcp4o6_port),
				  ntohs(dhcp4o6_port) + 1);
			dhcpv4_over_dhcpv6 = 1;
#endif /* DHCP4o6 */
524
#endif /* DHCPv6 */
Ted Lemon's avatar
Ted Lemon committed
525
#if defined (TRACING)
526 527
		} else if (!strcmp (argv [i], "-tf")) {
			if (++i == argc)
528
				usage(use_noarg, argv[i-1]);
529 530 531
			traceoutfile = argv [i];
		} else if (!strcmp (argv [i], "-play")) {
			if (++i == argc)
532
				usage(use_noarg, argv[i-1]);
533 534
			traceinfile = argv [i];
			trace_replay_init ();
Ted Lemon's avatar
Ted Lemon committed
535
#endif /* TRACING */
536
		} else if (argv [i][0] == '-') {
537
			usage("Unknown command %s", argv[i]);
538 539
		} else {
			struct interface_info *tmp =
540
				(struct interface_info *)0;
Shawn Routhier's avatar
Shawn Routhier committed
541 542 543 544
			if (strlen(argv[i]) >= sizeof(tmp->name))
				log_fatal("%s: interface name too long "
					  "(is %ld)",
					  argv[i], (long)strlen(argv[i]));
545 546 547 548 549
			result = interface_allocate (&tmp, MDL);
			if (result != ISC_R_SUCCESS)
				log_fatal ("Insufficient memory to %s %s: %s",
					   "record interface", argv [i],
					   isc_result_totext (result));
550
			strcpy (tmp -> name, argv [i]);
551 552 553 554 555 556
			if (interfaces) {
				interface_reference (&tmp -> next,
						     interfaces, MDL);
				interface_dereference (&interfaces, MDL);
			}
			interface_reference (&interfaces, tmp, MDL);
557 558
			tmp -> flags = INTERFACE_REQUESTED;
		}
Ted Lemon's avatar
Ted Lemon committed
559 560
	}

561 562 563 564 565 566 567 568 569 570 571 572
#if defined(DHCPv6) && defined(DHCP4o6)
	if (dhcpv4_over_dhcpv6) {
		if (!local_family_set)
			log_error("please specify the address family "
				  "with DHPv4 over DHCPv6 [-4|-6].");
		if ((local_family == AF_INET) && (interfaces != NULL))
			log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
				  "mode with command line specified "
				  "interfaces.");
	}
#endif /* DHCPv6 && DHCP4o6 */

573 574 575
	if (!no_dhcpd_conf && (s = getenv ("PATH_DHCPD_CONF"))) {
		path_dhcpd_conf = s;
	}
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602

#ifdef DHCPv6
        if (local_family == AF_INET6) {
                /* DHCPv6: override DHCPv4 lease and pid filenames */
	        if (!no_dhcpd_db) {
                        if ((s = getenv ("PATH_DHCPD6_DB")))
		                path_dhcpd_db = s;
                        else
		                path_dhcpd_db = _PATH_DHCPD6_DB;
	        }
	        if (!no_dhcpd_pid) {
                        if ((s = getenv ("PATH_DHCPD6_PID")))
		                path_dhcpd_pid = s;
                        else
		                path_dhcpd_pid = _PATH_DHCPD6_PID;
	        }
        } else
#else /* !DHCPv6 */
        {
	        if (!no_dhcpd_db && (s = getenv ("PATH_DHCPD_DB"))) {
		        path_dhcpd_db = s;
	        }
	        if (!no_dhcpd_pid && (s = getenv ("PATH_DHCPD_PID"))) {
		        path_dhcpd_pid = s;
	        }
        }
#endif /* DHCPv6 */
603

604 605 606 607 608
        /*
         * convert relative path names to absolute, for files that need
         * to be reopened after chdir() has been called
         */
        if (path_dhcpd_db[0] != '/') {
609 610
		const char *path = path_dhcpd_db;
                path_dhcpd_db = realpath(path_dhcpd_db, NULL);
611
                if (path_dhcpd_db == NULL)
612
                        log_fatal("Failed to get realpath for %s: %s", path,
613
                                   strerror(errno));
614 615
        }

616
	if (!quiet) {
617
		log_info("%s %s", message, PACKAGE_VERSION);
618 619 620
		log_info (copyright);
		log_info (arr);
		log_info (url);
Ted Lemon's avatar
Ted Lemon committed
621 622 623
	} else {
		log_perror = 0;
	}
624

625 626
#if defined (TRACING)
	trace_init (set_time, MDL);
627 628 629 630 631 632
	if (traceoutfile) {
		result = trace_begin (traceoutfile, MDL);
		if (result != ISC_R_SUCCESS)
			log_fatal ("Unable to begin trace: %s",
				isc_result_totext (result));
	}
633 634 635 636 637
	interface_trace_setup ();
	parse_trace_setup ();
	trace_srandom = trace_type_register ("random-seed", (void *)0,
					     trace_seed_input,
					     trace_seed_stop, MDL);
638
#if defined (NSUPDATE)
639
	trace_ddns_init();
640
#endif /* NSUPDATE */
641 642
#endif

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
#if defined (PARANOIA)
	/* get user and group info if those options were given */
	if (set_user) {
		struct passwd *tmp_pwd;

		if (geteuid())
			log_fatal ("you must be root to set user");

		if (!(tmp_pwd = getpwnam(set_user)))
			log_fatal ("no such user: %s", set_user);

		set_uid = tmp_pwd->pw_uid;

		/* use the user's group as the default gid */
		if (!set_group)
			set_gid = tmp_pwd->pw_gid;
	}

	if (set_group) {
/* get around the ISC declaration of group */
#define group real_group
		struct group *tmp_grp;

		if (geteuid())
			log_fatal ("you must be root to set group");

		if (!(tmp_grp = getgrnam(set_group)))
			log_fatal ("no such group: %s", set_group);

		set_gid = tmp_grp->gr_gid;
#undef group
	}

#  if defined (EARLY_CHROOT)
	if (set_chroot) setup_chroot (set_chroot);
#  endif /* EARLY_CHROOT */
#endif /* PARANOIA */

Ted Lemon's avatar
Ted Lemon committed
681
	/* Default to the DHCP/BOOTP port. */
682
	if (!local_port)
Ted Lemon's avatar
Ted Lemon committed
683
	{
684
		if ((s = getenv ("DHCPD_PORT"))) {
685
			local_port = validate_port (s);
686 687 688
			log_debug ("binding to environment-specified port %d",
				   ntohs (local_port));
		} else {
David Hankins's avatar
David Hankins committed
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
			if (local_family == AF_INET) {
				ent = getservbyname("dhcp", "udp");
				if (ent == NULL) {
					local_port = htons(67);
				} else {
					local_port = ent->s_port;
				}
			} else {
				/* INSIST(local_family == AF_INET6); */
				ent = getservbyname("dhcpv6-server", "udp");
				if (ent == NULL) {
					local_port = htons(547);
				} else {
					local_port = ent->s_port;
				}
			}
705
#ifndef __CYGWIN32__ /* XXX */
706
			endservent ();
707
#endif
708
		}
Ted Lemon's avatar
Ted Lemon committed
709
	}
710

David Hankins's avatar
David Hankins committed
711 712 713 714 715 716 717 718 719 720 721
  	if (local_family == AF_INET) {
		remote_port = htons(ntohs(local_port) + 1);
	} else {
		/* INSIST(local_family == AF_INET6); */
		ent = getservbyname("dhcpv6-client", "udp");
		if (ent == NULL) {
			remote_port = htons(546);
		} else {
			remote_port = ent->s_port;
		}
	}
722

723
	if (server) {
724 725 726 727
		if (local_family != AF_INET) {
			log_fatal("You can only specify address to send "
			          "replies to when running an IPv4 server.");
		}
728 729 730 731 732 733 734 735 736 737 738 739 740 741
		if (!inet_aton (server, &limited_broadcast)) {
			struct hostent *he;
			he = gethostbyname (server);
			if (he) {
				memcpy (&limited_broadcast,
					he -> h_addr_list [0],
					sizeof limited_broadcast);
			} else
				limited_broadcast.s_addr = INADDR_BROADCAST;
		}
	} else {
		limited_broadcast.s_addr = INADDR_BROADCAST;
	}

Ted Lemon's avatar
Ted Lemon committed
742
	/* Get the current time... */
Francis Dupont's avatar
Francis Dupont committed
743
	gettimeofday(&cur_tv, NULL);
Ted Lemon's avatar
Ted Lemon committed
744

Ted Lemon's avatar
Ted Lemon committed
745 746 747 748
	/* Set up the initial dhcp option universe. */
	initialize_common_option_spaces ();
	initialize_server_option_spaces ();

749 750
	/* Add the ddns update style enumeration prior to parsing. */
	add_enumeration (&ddns_styles);
Ted Lemon's avatar
Ted Lemon committed
751
	add_enumeration (&syslog_enum);
752 753 754 755 756 757 758 759
#if defined (LDAP_CONFIGURATION)
	add_enumeration (&ldap_methods);
#if defined (LDAP_USE_SSL)
	add_enumeration (&ldap_ssl_usage_enum);
	add_enumeration (&ldap_tls_reqcert_enum);
	add_enumeration (&ldap_tls_crlcheck_enum);
#endif
#endif
760

761 762 763
	if (!group_allocate (&root_group, MDL))
		log_fatal ("Can't allocate root group!");
	root_group -> authoritative = 0;
Ted Lemon's avatar
Ted Lemon committed
764

765 766 767
	/* Set up various hooks. */
	dhcp_interface_setup_hook = dhcpd_interface_setup_hook;
	bootp_packet_handler = do_packet;
768
#ifdef DHCPv6
769
	add_enumeration (&prefix_length_modes);
David Hankins's avatar
David Hankins committed
770
	dhcpv6_packet_handler = do_packet6;
771
#endif /* DHCPv6 */
772

773
#if defined (NSUPDATE)
Ted Lemon's avatar
Ted Lemon committed
774
	/* Set up the standard name service updater routine. */
775 776
	parse = NULL;
	status = new_parse(&parse, -1, std_nsupdate, sizeof(std_nsupdate) - 1,
777
			    "standard name service update routine", 0);
Ted Lemon's avatar
Ted Lemon committed
778
	if (status != ISC_R_SUCCESS)
779
		log_fatal ("can't begin parsing name service updater!");
Ted Lemon's avatar
Ted Lemon committed
780

781 782 783 784 785 786 787 788
	if (parse != NULL) {
		lose = 0;
		if (!(parse_executable_statements(&root_group->statements,
						  parse, &lose, context_any))) {
			end_parse(&parse);
			log_fatal("can't parse standard name service updater!");
		}
		end_parse(&parse);
Ted Lemon's avatar
Ted Lemon committed
789
	}
790
#endif
Ted Lemon's avatar
Ted Lemon committed
791

792
	/* Initialize icmp support... */
793 794
	if (!cftest && !lftest)
		icmp_startup (1, lease_pinged);
795 796 797 798 799 800 801 802

#if defined (TRACING)
	if (traceinfile) {
	    if (!no_dhcpd_db) {
		    log_error ("%s", "");
		    log_error ("** You must specify a lease file with -lf.");
		    log_error ("   Dhcpd will not overwrite your default");
		    log_fatal ("   lease file when playing back a trace. **");
803
	    }
804
	    trace_file_replay (traceinfile);
805

806
#if defined (DEBUG_MEMORY_LEAKAGE) && \
807 808
                defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
            free_everything ();
809
            omapi_print_dmalloc_usage_by_caller ();
810 811
#endif

812 813 814 815
	    exit (0);
	}
#endif

David Hankins's avatar
David Hankins committed
816
#ifdef DHCPv6
Francis Dupont's avatar
Francis Dupont committed
817
	/* set up DHCPv6 hashes */
818
	if (!ia_new_hash(&ia_na_active, DEFAULT_HASH_SIZE, MDL)) {
Francis Dupont's avatar
Francis Dupont committed
819 820
		log_fatal("Out of memory creating hash for active IA_NA.");
	}
821
	if (!ia_new_hash(&ia_ta_active, DEFAULT_HASH_SIZE, MDL)) {
Francis Dupont's avatar
Francis Dupont committed
822 823
		log_fatal("Out of memory creating hash for active IA_TA.");
	}
824
	if (!ia_new_hash(&ia_pd_active, DEFAULT_HASH_SIZE, MDL)) {
Francis Dupont's avatar
Francis Dupont committed
825
		log_fatal("Out of memory creating hash for active IA_PD.");
David Hankins's avatar
David Hankins committed
826 827 828
	}
#endif /* DHCPv6 */

Ted Lemon's avatar
Ted Lemon committed
829
	/* Read the dhcpd.conf file... */
Ted Lemon's avatar
Ted Lemon committed
830
	if (readconf () != ISC_R_SUCCESS)
831
		log_fatal ("Configuration file errors encountered -- exiting");
Ted Lemon's avatar
Ted Lemon committed
832

833
	postconf_initialization (quiet);
834

835 836 837 838
#if defined (FAILOVER_PROTOCOL)
	dhcp_failover_sanity_check();
#endif

839 840 841 842 843 844 845 846 847
#if defined(DHCPv6) && defined(DHCP4o6)
	if (dhcpv4_over_dhcpv6) {
		if ((local_family == AF_INET) && (interfaces != NULL))
			log_fatal("DHCPv4 server in DHPv4 over DHCPv6 "
				  "mode with config file specified "
				  "interfaces.");
	}
#endif /* DHCPv6 && DHCP4o6 */

848 849 850
#if defined (PARANOIA) && !defined (EARLY_CHROOT)
	if (set_chroot) setup_chroot (set_chroot);
#endif /* PARANOIA && !EARLY_CHROOT */
851

852 853 854 855 856
#ifdef DHCPv6
	/* log info about ipv6_ponds with large address ranges */
	report_jumbo_ranges();
#endif

857
        /* test option should cause an early exit */
858
	if (cftest && !lftest) {
859
 		exit(0);
860
	}
861

862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
	/*
	 * First part of dealing with pid files.  Check to see if
	 * we should continue running or not.  We run if:
	 * - we are testing the lease file out
	 * - we don't have a pid file to check
	 * - there is no other process running
	 */
	if ((lftest == 0) && (no_pid_file == ISC_FALSE)) {
		/*Read previous pid file. */
		if ((i = open(path_dhcpd_pid, O_RDONLY)) >= 0) {
			status = read(i, pbuf, (sizeof pbuf) - 1);
			close(i);
			if (status > 0) {
				pbuf[status] = 0;
				pid = atoi(pbuf);

				/*
				 * If there was a previous server process and
				 * it is still running, abort
				 */
				if (!pid ||
				    (pid != getpid() && kill(pid, 0) == 0))
					log_fatal("There's already a "
						  "DHCP server running.");
			}
		}
	}

890 891 892 893 894 895 896 897 898
	group_write_hook = group_writer;

	/* Start up the database... */
	db_startup (lftest);

	if (lftest)
		exit (0);

	/* Discover all the network interfaces and initialize them. */
899 900 901 902 903 904 905 906 907 908 909 910 911 912
#if defined(DHCPv6) && defined(DHCP4o6)
	if (dhcpv4_over_dhcpv6) {
		int real_family = local_family;
		local_family = AF_INET6;
		/* The DHCPv4 side of DHCPv4-over-DHCPv6 service
		   uses a specific discovery which doesn't register
		   DHCPv6 sockets. */
		if (real_family == AF_INET)
			discover_interfaces(DISCOVER_SERVER46);
		else
			discover_interfaces(DISCOVER_SERVER);
		local_family = real_family;
	} else
#endif /* DHCPv6 && DHCP4o6 */
David Hankins's avatar
David Hankins committed
913 914 915 916 917 918
	discover_interfaces(DISCOVER_SERVER);

#ifdef DHCPv6
	/*
	 * Remove addresses from our pools that we should not issue
	 * to clients.
919
	 *
920 921
	 * We currently have no support for this in IPv4. It is not
	 * as important in IPv4, as making pools with ranges that
922 923
	 * leave out interfaces and hosts is fairly straightforward
	 * using range notation, but not so handy with CIDR notation.
David Hankins's avatar
David Hankins committed
924
	 */
925 926
	if (local_family == AF_INET6) {
		mark_hosts_unavailable();
927
		mark_phosts_unavailable();
928 929
		mark_interfaces_unavailable();
	}
David Hankins's avatar
David Hankins committed
930 931
#endif /* DHCPv6 */

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
	/* Make up a seed for the random number generator from current
	   time plus the sum of the last four bytes of each
	   interface's hardware address interpreted as an integer.
	   Not much entropy, but we're booting, so we're not likely to
	   find anything better. */
	seed = 0;
	for (ip = interfaces; ip; ip = ip -> next) {
		int junk;
		memcpy (&junk,
			&ip -> hw_address.hbuf [ip -> hw_address.hlen -
					       sizeof seed], sizeof seed);
		seed += junk;
	}
	srandom (seed + cur_time);
#if defined (TRACING)
	trace_seed_stash (trace_srandom, seed + cur_time);
#endif
949
	postdb_startup ();
950

951
#ifdef DHCPv6
David Hankins's avatar
David Hankins committed
952
	/*
953 954 955 956 957
	 * Set server DHCPv6 identifier - we go in order:
	 * dhcp6.server-id in the config file
	 * server-duid from the lease file
	 * server-duid from the config file (the config file is read first
	 * and the lease file overwrites the config file information)
958
	 * generate a new one from the interface hardware addresses.
959
	 * In all cases we write it out to the lease file.
David Hankins's avatar
David Hankins committed
960 961
	 * See dhcpv6.c for discussion of setting DUID.
	 */
962 963 964 965
	if ((set_server_duid_from_option() != ISC_R_SUCCESS) &&
	    (!server_duid_isset()) &&
	    (generate_new_server_duid() != ISC_R_SUCCESS)) {
		log_fatal("Unable to set server identifier.");
David Hankins's avatar
David Hankins committed
966
	}
967
	write_server_duid();
968 969 970 971
#ifdef DHCP4o6
	if (dhcpv4_over_dhcpv6)
		dhcp4o6_setup(dhcp4o6_port);
#endif /* DHCP4o6 */
972
#endif /* DHCPv6 */
David Hankins's avatar
David Hankins committed
973

974
#ifndef DEBUG
975
	/*
976 977 978
	 * Second part of dealing with pid files.  Now
	 * that we have forked we can write our pid if
	 * appropriate.
979 980 981 982 983
	 */
	if (no_pid_file == ISC_FALSE) {
		i = open(path_dhcpd_pid, O_WRONLY|O_CREAT|O_TRUNC, 0644);
		if (i >= 0) {
			sprintf(pbuf, "%d\n", (int) getpid());
984
			IGNORE_RET(write(i, pbuf, strlen(pbuf)));
985 986 987 988 989 990
			close(i);
		} else {
			log_error("Can't create PID file %s: %m.",
				  path_dhcpd_pid);
		}
	}
991

992 993 994 995 996 997 998 999
#if defined (PARANOIA)
	/* change uid to the specified one */

	if (set_gid) {
		if (setgroups (0, (void *)0))
			log_fatal ("setgroups: %m");
		if (setgid (set_gid))
			log_fatal ("setgid(%d): %m", (int) set_gid);
1000
	}
1001 1002 1003 1004 1005 1006 1007

	if (set_uid) {
		if (setuid (set_uid))
			log_fatal ("setuid(%d): %m", (int) set_uid);
	}
#endif /* PARANOIA */

1008 1009 1010 1011 1012 1013 1014 1015
	/* If we were requested to log to stdout on the command line,
	   keep doing so; otherwise, stop. */
	if (log_perror == -1)
		log_perror = 1;
	else
		log_perror = 0;

	if (daemon) {
1016 1017 1018 1019 1020 1021 1022 1023 1024
		if (dfd[0] != -1 && dfd[1] != -1) {
			char buf = 0;

			if (write(dfd[1], &buf, 1) != 1)
				log_fatal("write to parent: %m");
			(void) close(dfd[1]);
			dfd[0] = dfd[1] = -1;
		}

1025
		/* Become session leader and get pid... */
1026
		(void) setsid();
1027 1028

                /* Close standard I/O descriptors. */
1029 1030 1031
                (void) close(0);
                (void) close(1);
                (void) close(2);
1032 1033

                /* Reopen them on /dev/null. */
1034 1035 1036
                (void) open("/dev/null", O_RDWR);
                (void) open("/dev/null", O_RDWR);
                (void) open("/dev/null", O_RDWR);
1037
                log_perror = 0; /* No sense logging to /dev/null. */
1038

1039
       		IGNORE_RET (chdir("/"));
Ted Lemon's avatar