1147072Sbrooks/*	$OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $	*/
2147072Sbrooks
3147072Sbrooks/*
4147072Sbrooks * Copyright 2004 Henning Brauer <henning@openbsd.org>
5147072Sbrooks * Copyright (c) 1995, 1996, 1997, 1998, 1999
6147072Sbrooks * The Internet Software Consortium.    All rights reserved.
7147072Sbrooks *
8147072Sbrooks * Redistribution and use in source and binary forms, with or without
9147072Sbrooks * modification, are permitted provided that the following conditions
10147072Sbrooks * are met:
11147072Sbrooks *
12147072Sbrooks * 1. Redistributions of source code must retain the above copyright
13147072Sbrooks *    notice, this list of conditions and the following disclaimer.
14147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
15147072Sbrooks *    notice, this list of conditions and the following disclaimer in the
16147072Sbrooks *    documentation and/or other materials provided with the distribution.
17147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names
18147072Sbrooks *    of its contributors may be used to endorse or promote products derived
19147072Sbrooks *    from this software without specific prior written permission.
20147072Sbrooks *
21147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
22147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25147072Sbrooks * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
26147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33147072Sbrooks * SUCH DAMAGE.
34147072Sbrooks *
35147072Sbrooks * This software has been written for the Internet Software Consortium
36147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
37147072Sbrooks * Enterprises.  To learn more about the Internet Software Consortium,
38147072Sbrooks * see ``http://www.vix.com/isc''.  To learn more about Vixie
39147072Sbrooks * Enterprises, see ``http://www.vix.com''.
40147072Sbrooks *
41147072Sbrooks * This client was substantially modified and enhanced by Elliot Poger
42147072Sbrooks * for use on Linux while he was working on the MosquitoNet project at
43147072Sbrooks * Stanford.
44147072Sbrooks *
45147072Sbrooks * The current version owes much to Elliot's Linux enhancements, but
46147072Sbrooks * was substantially reorganized and partially rewritten by Ted Lemon
47147072Sbrooks * so as to use the same networking framework that the Internet Software
48147072Sbrooks * Consortium DHCP server uses.   Much system-specific configuration code
49147072Sbrooks * was moved into a shell script so that as support for more operating
50147072Sbrooks * systems is added, it will not be necessary to port and maintain
51147072Sbrooks * system-specific configuration code to these operating systems - instead,
52147072Sbrooks * the shell script can invoke the native tools to accomplish the same
53147072Sbrooks * purpose.
54147072Sbrooks */
55147072Sbrooks
56149399Sbrooks#include <sys/cdefs.h>
57149399Sbrooks__FBSDID("$FreeBSD: stable/10/sbin/dhclient/dhclient.c 330693 2018-03-09 14:39:28Z dab $");
58149399Sbrooks
59280250Srwatson#include <sys/capsicum.h>
60255219Spjd
61147072Sbrooks#include "dhcpd.h"
62147072Sbrooks#include "privsep.h"
63147072Sbrooks
64280250Srwatson#include <sys/capsicum.h>
65252629Spjd
66147085Sbrooks#include <net80211/ieee80211_freebsd.h>
67147085Sbrooks
68147085Sbrooks#ifndef _PATH_VAREMPTY
69147085Sbrooks#define	_PATH_VAREMPTY	"/var/empty"
70147085Sbrooks#endif
71147085Sbrooks
72147072Sbrooks#define	PERIOD 0x2e
73147072Sbrooks#define	hyphenchar(c) ((c) == 0x2d)
74147072Sbrooks#define	bslashchar(c) ((c) == 0x5c)
75147072Sbrooks#define	periodchar(c) ((c) == PERIOD)
76147072Sbrooks#define	asterchar(c) ((c) == 0x2a)
77147072Sbrooks#define	alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
78147072Sbrooks	    ((c) >= 0x61 && (c) <= 0x7a))
79147072Sbrooks#define	digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
80149639Sbrooks#define	whitechar(c) ((c) == ' ' || (c) == '\t')
81147072Sbrooks
82147072Sbrooks#define	borderchar(c) (alphachar(c) || digitchar(c))
83147072Sbrooks#define	middlechar(c) (borderchar(c) || hyphenchar(c))
84147072Sbrooks#define	domainchar(c) ((c) > 0x20 && (c) < 0x7f)
85147072Sbrooks
86147072Sbrooks#define	CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
87147072Sbrooks
88147072Sbrookstime_t cur_time;
89147072Sbrookstime_t default_lease_time = 43200; /* 12 hours... */
90147072Sbrooks
91147072Sbrookschar *path_dhclient_conf = _PATH_DHCLIENT_CONF;
92147072Sbrookschar *path_dhclient_db = NULL;
93147072Sbrooks
94147072Sbrooksint log_perror = 1;
95147072Sbrooksint privfd;
96147072Sbrooksint nullfd = -1;
97147072Sbrooks
98252623Spjdchar hostname[_POSIX_HOST_NAME_MAX + 1];
99252623Spjd
100147072Sbrooksstruct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
101252616Spjdstruct in_addr inaddr_any, inaddr_broadcast;
102147072Sbrooks
103226345Sdeschar *path_dhclient_pidfile;
104226345Sdesstruct pidfh *pidfile;
105226345Sdes
106147072Sbrooks/*
107147072Sbrooks * ASSERT_STATE() does nothing now; it used to be
108147072Sbrooks * assert (state_is == state_shouldbe).
109147072Sbrooks */
110147072Sbrooks#define ASSERT_STATE(state_is, state_shouldbe) {}
111147072Sbrooks
112147072Sbrooks#define TIME_MAX 2147483647
113147072Sbrooks
114147072Sbrooksint		log_priority;
115147072Sbrooksint		no_daemon;
116147072Sbrooksint		unknown_ok = 1;
117147072Sbrooksint		routefd;
118147072Sbrooks
119147072Sbrooksstruct interface_info	*ifi;
120147072Sbrooks
121147072Sbrooksint		 findproto(char *, int);
122147072Sbrooksstruct sockaddr	*get_ifa(char *, int);
123147072Sbrooksvoid		 routehandler(struct protocol *);
124147072Sbrooksvoid		 usage(void);
125147072Sbrooksint		 check_option(struct client_lease *l, int option);
126166602Semasteint		 check_classless_option(unsigned char *data, int len);
127147072Sbrooksint		 ipv4addrs(char * buf);
128147072Sbrooksint		 res_hnok(const char *dn);
129149639Sbrooksint		 check_search(const char *srch);
130147072Sbrookschar		*option_as_string(unsigned int code, unsigned char *data, int len);
131147072Sbrooksint		 fork_privchld(int, int);
132147072Sbrooks
133147072Sbrooks#define	ROUNDUP(a) \
134147072Sbrooks	    ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
135147072Sbrooks#define	ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
136147072Sbrooks
137209756Sbrianstatic time_t	scripttime;
138147072Sbrooks
139147072Sbrooksint
140147072Sbrooksfindproto(char *cp, int n)
141147072Sbrooks{
142147072Sbrooks	struct sockaddr *sa;
143147072Sbrooks	int i;
144147072Sbrooks
145147072Sbrooks	if (n == 0)
146147072Sbrooks		return -1;
147147072Sbrooks	for (i = 1; i; i <<= 1) {
148147072Sbrooks		if (i & n) {
149147072Sbrooks			sa = (struct sockaddr *)cp;
150147072Sbrooks			switch (i) {
151147072Sbrooks			case RTA_IFA:
152147072Sbrooks			case RTA_DST:
153147072Sbrooks			case RTA_GATEWAY:
154147072Sbrooks			case RTA_NETMASK:
155147072Sbrooks				if (sa->sa_family == AF_INET)
156147072Sbrooks					return AF_INET;
157147072Sbrooks				if (sa->sa_family == AF_INET6)
158147072Sbrooks					return AF_INET6;
159147072Sbrooks				break;
160147072Sbrooks			case RTA_IFP:
161147072Sbrooks				break;
162147072Sbrooks			}
163147072Sbrooks			ADVANCE(cp, sa);
164147072Sbrooks		}
165147072Sbrooks	}
166147072Sbrooks	return (-1);
167147072Sbrooks}
168147072Sbrooks
169147072Sbrooksstruct sockaddr *
170147072Sbrooksget_ifa(char *cp, int n)
171147072Sbrooks{
172147072Sbrooks	struct sockaddr *sa;
173147072Sbrooks	int i;
174147072Sbrooks
175147072Sbrooks	if (n == 0)
176147072Sbrooks		return (NULL);
177147072Sbrooks	for (i = 1; i; i <<= 1)
178147072Sbrooks		if (i & n) {
179147072Sbrooks			sa = (struct sockaddr *)cp;
180147072Sbrooks			if (i == RTA_IFA)
181147072Sbrooks				return (sa);
182147072Sbrooks			ADVANCE(cp, sa);
183147072Sbrooks		}
184147072Sbrooks
185147072Sbrooks	return (NULL);
186147072Sbrooks}
187177501Ssam
188147072Sbrooksstruct iaddr defaddr = { 4 };
189177501Ssamuint8_t curbssid[6];
190147072Sbrooks
191177501Ssamstatic void
192177501Ssamdisassoc(void *arg)
193177501Ssam{
194177501Ssam	struct interface_info *ifi = arg;
195177501Ssam
196177501Ssam	/*
197177501Ssam	 * Clear existing state.
198177501Ssam	 */
199177501Ssam	if (ifi->client->active != NULL) {
200177501Ssam		script_init("EXPIRE", NULL);
201177501Ssam		script_write_params("old_",
202177501Ssam		    ifi->client->active);
203177501Ssam		if (ifi->client->alias)
204177501Ssam			script_write_params("alias_",
205177501Ssam				ifi->client->alias);
206177501Ssam		script_go();
207177501Ssam	}
208177501Ssam	ifi->client->state = S_INIT;
209177501Ssam}
210177501Ssam
211147072Sbrooks/* ARGSUSED */
212147072Sbrooksvoid
213147072Sbrooksroutehandler(struct protocol *p)
214147072Sbrooks{
215209756Sbrian	char msg[2048], *addr;
216147072Sbrooks	struct rt_msghdr *rtm;
217147072Sbrooks	struct if_msghdr *ifm;
218147072Sbrooks	struct ifa_msghdr *ifam;
219147072Sbrooks	struct if_announcemsghdr *ifan;
220177501Ssam	struct ieee80211_join_event *jev;
221147072Sbrooks	struct client_lease *l;
222147072Sbrooks	time_t t = time(NULL);
223147072Sbrooks	struct sockaddr *sa;
224147072Sbrooks	struct iaddr a;
225147072Sbrooks	ssize_t n;
226239564Sjhb	int linkstat;
227147072Sbrooks
228147072Sbrooks	n = read(routefd, &msg, sizeof(msg));
229147072Sbrooks	rtm = (struct rt_msghdr *)msg;
230147072Sbrooks	if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen ||
231147072Sbrooks	    rtm->rtm_version != RTM_VERSION)
232147072Sbrooks		return;
233147072Sbrooks
234147072Sbrooks	switch (rtm->rtm_type) {
235147072Sbrooks	case RTM_NEWADDR:
236154161Sbrooks	case RTM_DELADDR:
237147072Sbrooks		ifam = (struct ifa_msghdr *)rtm;
238154161Sbrooks
239147072Sbrooks		if (ifam->ifam_index != ifi->index)
240147072Sbrooks			break;
241147072Sbrooks		if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET)
242147072Sbrooks			break;
243154161Sbrooks		if (scripttime == 0 || t < scripttime + 10)
244154161Sbrooks			break;
245154161Sbrooks
246147072Sbrooks		sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs);
247147072Sbrooks		if (sa == NULL)
248209756Sbrian			break;
249147072Sbrooks
250147072Sbrooks		if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf))
251147072Sbrooks			error("king bula sez: len mismatch");
252147072Sbrooks		memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len);
253147072Sbrooks		if (addr_eq(a, defaddr))
254147072Sbrooks			break;
255147072Sbrooks
256147072Sbrooks		for (l = ifi->client->active; l != NULL; l = l->next)
257147072Sbrooks			if (addr_eq(a, l->address))
258147072Sbrooks				break;
259147072Sbrooks
260209756Sbrian		if (l == NULL)	/* added/deleted addr is not the one we set */
261147072Sbrooks			break;
262209756Sbrian
263209756Sbrian		addr = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
264209756Sbrian		if (rtm->rtm_type == RTM_NEWADDR)  {
265209756Sbrian			/*
266209756Sbrian			 * XXX: If someone other than us adds our address,
267209756Sbrian			 * should we assume they are taking over from us,
268209756Sbrian			 * delete the lease record, and exit without modifying
269209756Sbrian			 * the interface?
270209756Sbrian			 */
271209756Sbrian			warning("My address (%s) was re-added", addr);
272209756Sbrian		} else {
273209756Sbrian			warning("My address (%s) was deleted, dhclient exiting",
274209756Sbrian			    addr);
275209756Sbrian			goto die;
276209756Sbrian		}
277209756Sbrian		break;
278147072Sbrooks	case RTM_IFINFO:
279147072Sbrooks		ifm = (struct if_msghdr *)rtm;
280147072Sbrooks		if (ifm->ifm_index != ifi->index)
281147072Sbrooks			break;
282209756Sbrian		if ((rtm->rtm_flags & RTF_UP) == 0) {
283209756Sbrian			warning("Interface %s is down, dhclient exiting",
284209756Sbrian			    ifi->name);
285147072Sbrooks			goto die;
286209756Sbrian		}
287239564Sjhb		linkstat = interface_link_status(ifi->name);
288239564Sjhb		if (linkstat != ifi->linkstat) {
289239564Sjhb			debug("%s link state %s -> %s", ifi->name,
290239564Sjhb			    ifi->linkstat ? "up" : "down",
291239564Sjhb			    linkstat ? "up" : "down");
292239564Sjhb			ifi->linkstat = linkstat;
293239564Sjhb			if (linkstat)
294239564Sjhb				state_reboot(ifi);
295239356Sjhb		}
296147072Sbrooks		break;
297147072Sbrooks	case RTM_IFANNOUNCE:
298147072Sbrooks		ifan = (struct if_announcemsghdr *)rtm;
299147072Sbrooks		if (ifan->ifan_what == IFAN_DEPARTURE &&
300209756Sbrian		    ifan->ifan_index == ifi->index) {
301209756Sbrian			warning("Interface %s is gone, dhclient exiting",
302209756Sbrian			    ifi->name);
303147072Sbrooks			goto die;
304209756Sbrian		}
305147072Sbrooks		break;
306147085Sbrooks	case RTM_IEEE80211:
307147085Sbrooks		ifan = (struct if_announcemsghdr *)rtm;
308147085Sbrooks		if (ifan->ifan_index != ifi->index)
309147085Sbrooks			break;
310147085Sbrooks		switch (ifan->ifan_what) {
311147085Sbrooks		case RTM_IEEE80211_ASSOC:
312148373Ssam		case RTM_IEEE80211_REASSOC:
313147085Sbrooks			/*
314177501Ssam			 * Use assoc/reassoc event to kick state machine
315177501Ssam			 * in case we roam.  Otherwise fall back to the
316177501Ssam			 * normal state machine just like a wired network.
317147085Sbrooks			 */
318177501Ssam			jev = (struct ieee80211_join_event *) &ifan[1];
319177501Ssam			if (memcmp(curbssid, jev->iev_addr, 6)) {
320177501Ssam				disassoc(ifi);
321177501Ssam				state_reboot(ifi);
322147351Sbrooks			}
323177501Ssam			memcpy(curbssid, jev->iev_addr, 6);
324147085Sbrooks			break;
325147085Sbrooks		}
326147085Sbrooks		break;
327147072Sbrooks	default:
328147072Sbrooks		break;
329147072Sbrooks	}
330147072Sbrooks	return;
331147072Sbrooks
332147072Sbrooksdie:
333147072Sbrooks	script_init("FAIL", NULL);
334147072Sbrooks	if (ifi->client->alias)
335147072Sbrooks		script_write_params("alias_", ifi->client->alias);
336147072Sbrooks	script_go();
337226345Sdes	if (pidfile != NULL)
338226345Sdes		pidfile_remove(pidfile);
339147072Sbrooks	exit(1);
340147072Sbrooks}
341147072Sbrooks
342147072Sbrooksint
343147072Sbrooksmain(int argc, char *argv[])
344147072Sbrooks{
345147072Sbrooks	extern char		*__progname;
346147072Sbrooks	int			 ch, fd, quiet = 0, i = 0;
347147072Sbrooks	int			 pipe_fd[2];
348147085Sbrooks	int			 immediate_daemon = 0;
349147072Sbrooks	struct passwd		*pw;
350226345Sdes	pid_t			 otherpid;
351255219Spjd	cap_rights_t		 rights;
352147072Sbrooks
353147072Sbrooks	/* Initially, log errors to stderr as well as to syslogd. */
354147072Sbrooks	openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
355177500Ssam	setlogmask(LOG_UPTO(LOG_DEBUG));
356147072Sbrooks
357226345Sdes	while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1)
358147072Sbrooks		switch (ch) {
359147085Sbrooks		case 'b':
360147085Sbrooks			immediate_daemon = 1;
361147085Sbrooks			break;
362147072Sbrooks		case 'c':
363147072Sbrooks			path_dhclient_conf = optarg;
364147072Sbrooks			break;
365147072Sbrooks		case 'd':
366147072Sbrooks			no_daemon = 1;
367147072Sbrooks			break;
368147072Sbrooks		case 'l':
369147072Sbrooks			path_dhclient_db = optarg;
370147072Sbrooks			break;
371226345Sdes		case 'p':
372226345Sdes			path_dhclient_pidfile = optarg;
373226345Sdes			break;
374147072Sbrooks		case 'q':
375147072Sbrooks			quiet = 1;
376147072Sbrooks			break;
377147072Sbrooks		case 'u':
378147072Sbrooks			unknown_ok = 0;
379147072Sbrooks			break;
380147072Sbrooks		default:
381147072Sbrooks			usage();
382147072Sbrooks		}
383147072Sbrooks
384147072Sbrooks	argc -= optind;
385147072Sbrooks	argv += optind;
386147072Sbrooks
387147072Sbrooks	if (argc != 1)
388147072Sbrooks		usage();
389147072Sbrooks
390226345Sdes	if (path_dhclient_pidfile == NULL) {
391226345Sdes		asprintf(&path_dhclient_pidfile,
392226345Sdes		    "%sdhclient.%s.pid", _PATH_VARRUN, *argv);
393226345Sdes		if (path_dhclient_pidfile == NULL)
394226345Sdes			error("asprintf");
395226345Sdes	}
396226345Sdes	pidfile = pidfile_open(path_dhclient_pidfile, 0600, &otherpid);
397226345Sdes	if (pidfile == NULL) {
398226345Sdes		if (errno == EEXIST)
399226345Sdes			error("dhclient already running, pid: %d.", otherpid);
400226345Sdes		if (errno == EAGAIN)
401226345Sdes			error("dhclient already running.");
402226345Sdes		warning("Cannot open or create pidfile: %m");
403226345Sdes	}
404226345Sdes
405147072Sbrooks	if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL)
406147072Sbrooks		error("calloc");
407147072Sbrooks	if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ)
408147072Sbrooks		error("Interface name too long");
409147072Sbrooks	if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s",
410147072Sbrooks	    _PATH_DHCLIENT_DB, ifi->name) == -1)
411147072Sbrooks		error("asprintf");
412147072Sbrooks
413147072Sbrooks	if (quiet)
414147072Sbrooks		log_perror = 0;
415147072Sbrooks
416147072Sbrooks	tzset();
417147072Sbrooks	time(&cur_time);
418147072Sbrooks
419252616Spjd	inaddr_broadcast.s_addr = INADDR_BROADCAST;
420147072Sbrooks	inaddr_any.s_addr = INADDR_ANY;
421147072Sbrooks
422147072Sbrooks	read_client_conf();
423147072Sbrooks
424226345Sdes	/* The next bit is potentially very time-consuming, so write out
425226345Sdes	   the pidfile right away.  We will write it out again with the
426226345Sdes	   correct pid after daemonizing. */
427226345Sdes	if (pidfile != NULL)
428226345Sdes		pidfile_write(pidfile);
429226345Sdes
430147072Sbrooks	if (!interface_link_status(ifi->name)) {
431147072Sbrooks		fprintf(stderr, "%s: no link ...", ifi->name);
432147072Sbrooks		fflush(stderr);
433147072Sbrooks		sleep(1);
434147072Sbrooks		while (!interface_link_status(ifi->name)) {
435147072Sbrooks			fprintf(stderr, ".");
436147072Sbrooks			fflush(stderr);
437147072Sbrooks			if (++i > 10) {
438161514Sbrian				fprintf(stderr, " giving up\n");
439161514Sbrian				exit(1);
440147072Sbrooks			}
441147072Sbrooks			sleep(1);
442147072Sbrooks		}
443161514Sbrian		fprintf(stderr, " got link\n");
444147072Sbrooks	}
445239564Sjhb	ifi->linkstat = 1;
446147072Sbrooks
447147072Sbrooks	if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
448147072Sbrooks		error("cannot open %s: %m", _PATH_DEVNULL);
449147072Sbrooks
450147072Sbrooks	if ((pw = getpwnam("_dhcp")) == NULL) {
451147072Sbrooks		warning("no such user: _dhcp, falling back to \"nobody\"");
452147072Sbrooks		if ((pw = getpwnam("nobody")) == NULL)
453147072Sbrooks			error("no such user: nobody");
454147072Sbrooks	}
455147072Sbrooks
456252623Spjd	/*
457252623Spjd	 * Obtain hostname before entering capability mode - it won't be
458252623Spjd	 * possible then, as reading kern.hostname is not permitted.
459252623Spjd	 */
460252623Spjd	if (gethostname(hostname, sizeof(hostname)) < 0)
461252623Spjd		hostname[0] = '\0';
462252623Spjd
463252697Spjd	priv_script_init("PREINIT", NULL);
464252697Spjd	if (ifi->client->alias)
465252697Spjd		priv_script_write_params("alias_", ifi->client->alias);
466252697Spjd	priv_script_go();
467252697Spjd
468252626Spjd	/* set up the interface */
469252626Spjd	discover_interfaces(ifi);
470252626Spjd
471147072Sbrooks	if (pipe(pipe_fd) == -1)
472147072Sbrooks		error("pipe");
473147072Sbrooks
474147072Sbrooks	fork_privchld(pipe_fd[0], pipe_fd[1]);
475147072Sbrooks
476252626Spjd	close(ifi->ufdesc);
477252626Spjd	ifi->ufdesc = -1;
478252626Spjd	close(ifi->wfdesc);
479252626Spjd	ifi->wfdesc = -1;
480252626Spjd
481147072Sbrooks	close(pipe_fd[0]);
482147072Sbrooks	privfd = pipe_fd[1];
483255219Spjd	cap_rights_init(&rights, CAP_READ, CAP_WRITE);
484255219Spjd	if (cap_rights_limit(privfd, &rights) < 0 && errno != ENOSYS)
485252629Spjd		error("can't limit private descriptor: %m");
486147072Sbrooks
487147072Sbrooks	if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
488147072Sbrooks		error("can't open and lock %s: %m", path_dhclient_db);
489147072Sbrooks	read_client_leases();
490147072Sbrooks	rewrite_client_leases();
491147072Sbrooks	close(fd);
492147072Sbrooks
493147072Sbrooks	if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
494147072Sbrooks		add_protocol("AF_ROUTE", routefd, routehandler, ifi);
495252625Spjd	if (shutdown(routefd, SHUT_WR) < 0)
496252625Spjd		error("can't shutdown route socket: %m");
497261828Sbrueffer	cap_rights_init(&rights, CAP_EVENT, CAP_READ);
498255219Spjd	if (cap_rights_limit(routefd, &rights) < 0 && errno != ENOSYS)
499252630Spjd		error("can't limit route socket: %m");
500147072Sbrooks
501147072Sbrooks	if (chroot(_PATH_VAREMPTY) == -1)
502147072Sbrooks		error("chroot");
503147072Sbrooks	if (chdir("/") == -1)
504147072Sbrooks		error("chdir(\"/\")");
505147072Sbrooks
506147072Sbrooks	if (setgroups(1, &pw->pw_gid) ||
507147072Sbrooks	    setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
508147072Sbrooks	    seteuid(pw->pw_uid) || setuid(pw->pw_uid))
509147072Sbrooks		error("can't drop privileges: %m");
510147072Sbrooks
511147072Sbrooks	endpwent();
512147072Sbrooks
513147072Sbrooks	setproctitle("%s", ifi->name);
514147072Sbrooks
515252634Spjd	if (cap_enter() < 0 && errno != ENOSYS)
516252634Spjd		error("can't enter capability mode: %m");
517252634Spjd
518147085Sbrooks	if (immediate_daemon)
519147085Sbrooks		go_daemon();
520147085Sbrooks
521147072Sbrooks	ifi->client->state = S_INIT;
522147072Sbrooks	state_reboot(ifi);
523147072Sbrooks
524147072Sbrooks	bootp_packet_handler = do_packet;
525147072Sbrooks
526147072Sbrooks	dispatch();
527147072Sbrooks
528147072Sbrooks	/* not reached */
529147072Sbrooks	return (0);
530147072Sbrooks}
531147072Sbrooks
532147072Sbrooksvoid
533147072Sbrooksusage(void)
534147072Sbrooks{
535147072Sbrooks	extern char	*__progname;
536147072Sbrooks
537161514Sbrian	fprintf(stderr, "usage: %s [-bdqu] ", __progname);
538147072Sbrooks	fprintf(stderr, "[-c conffile] [-l leasefile] interface\n");
539147072Sbrooks	exit(1);
540147072Sbrooks}
541147072Sbrooks
542147072Sbrooks/*
543147072Sbrooks * Individual States:
544147072Sbrooks *
545147072Sbrooks * Each routine is called from the dhclient_state_machine() in one of
546147072Sbrooks * these conditions:
547147072Sbrooks * -> entering INIT state
548147072Sbrooks * -> recvpacket_flag == 0: timeout in this state
549147072Sbrooks * -> otherwise: received a packet in this state
550147072Sbrooks *
551147072Sbrooks * Return conditions as handled by dhclient_state_machine():
552147072Sbrooks * Returns 1, sendpacket_flag = 1: send packet, reset timer.
553147072Sbrooks * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
554147072Sbrooks * Returns 0: finish the nap which was interrupted for no good reason.
555147072Sbrooks *
556147072Sbrooks * Several per-interface variables are used to keep track of the process:
557147072Sbrooks *   active_lease: the lease that is being used on the interface
558147072Sbrooks *                 (null pointer if not configured yet).
559147072Sbrooks *   offered_leases: leases corresponding to DHCPOFFER messages that have
560147072Sbrooks *                   been sent to us by DHCP servers.
561147072Sbrooks *   acked_leases: leases corresponding to DHCPACK messages that have been
562147072Sbrooks *                 sent to us by DHCP servers.
563147072Sbrooks *   sendpacket: DHCP packet we're trying to send.
564147072Sbrooks *   destination: IP address to send sendpacket to
565147072Sbrooks * In addition, there are several relevant per-lease variables.
566147072Sbrooks *   T1_expiry, T2_expiry, lease_expiry: lease milestones
567147072Sbrooks * In the active lease, these control the process of renewing the lease;
568147072Sbrooks * In leases on the acked_leases list, this simply determines when we
569147072Sbrooks * can no longer legitimately use the lease.
570147072Sbrooks */
571147072Sbrooks
572147072Sbrooksvoid
573147072Sbrooksstate_reboot(void *ipp)
574147072Sbrooks{
575147072Sbrooks	struct interface_info *ip = ipp;
576147072Sbrooks
577147072Sbrooks	/* If we don't remember an active lease, go straight to INIT. */
578147072Sbrooks	if (!ip->client->active || ip->client->active->is_bootp) {
579147072Sbrooks		state_init(ip);
580147072Sbrooks		return;
581147072Sbrooks	}
582147072Sbrooks
583147072Sbrooks	/* We are in the rebooting state. */
584147072Sbrooks	ip->client->state = S_REBOOTING;
585147072Sbrooks
586147072Sbrooks	/* make_request doesn't initialize xid because it normally comes
587147072Sbrooks	   from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
588147072Sbrooks	   so pick an xid now. */
589147072Sbrooks	ip->client->xid = arc4random();
590147072Sbrooks
591147072Sbrooks	/* Make a DHCPREQUEST packet, and set appropriate per-interface
592147072Sbrooks	   flags. */
593147072Sbrooks	make_request(ip, ip->client->active);
594147072Sbrooks	ip->client->destination = iaddr_broadcast;
595147072Sbrooks	ip->client->first_sending = cur_time;
596147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
597147072Sbrooks
598147072Sbrooks	/* Zap the medium list... */
599147072Sbrooks	ip->client->medium = NULL;
600147072Sbrooks
601147072Sbrooks	/* Send out the first DHCPREQUEST packet. */
602147072Sbrooks	send_request(ip);
603147072Sbrooks}
604147072Sbrooks
605147072Sbrooks/*
606147072Sbrooks * Called when a lease has completely expired and we've
607147072Sbrooks * been unable to renew it.
608147072Sbrooks */
609147072Sbrooksvoid
610147072Sbrooksstate_init(void *ipp)
611147072Sbrooks{
612147072Sbrooks	struct interface_info *ip = ipp;
613147072Sbrooks
614147072Sbrooks	ASSERT_STATE(state, S_INIT);
615147072Sbrooks
616147072Sbrooks	/* Make a DHCPDISCOVER packet, and set appropriate per-interface
617147072Sbrooks	   flags. */
618147072Sbrooks	make_discover(ip, ip->client->active);
619147072Sbrooks	ip->client->xid = ip->client->packet.xid;
620147072Sbrooks	ip->client->destination = iaddr_broadcast;
621147072Sbrooks	ip->client->state = S_SELECTING;
622147072Sbrooks	ip->client->first_sending = cur_time;
623147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
624147072Sbrooks
625147072Sbrooks	/* Add an immediate timeout to cause the first DHCPDISCOVER packet
626147072Sbrooks	   to go out. */
627147072Sbrooks	send_discover(ip);
628147072Sbrooks}
629147072Sbrooks
630147072Sbrooks/*
631147072Sbrooks * state_selecting is called when one or more DHCPOFFER packets
632147072Sbrooks * have been received and a configurable period of time has passed.
633147072Sbrooks */
634147072Sbrooksvoid
635147072Sbrooksstate_selecting(void *ipp)
636147072Sbrooks{
637147072Sbrooks	struct interface_info *ip = ipp;
638147072Sbrooks	struct client_lease *lp, *next, *picked;
639147072Sbrooks
640147072Sbrooks	ASSERT_STATE(state, S_SELECTING);
641147072Sbrooks
642147072Sbrooks	/* Cancel state_selecting and send_discover timeouts, since either
643147072Sbrooks	   one could have got us here. */
644147072Sbrooks	cancel_timeout(state_selecting, ip);
645147072Sbrooks	cancel_timeout(send_discover, ip);
646147072Sbrooks
647147072Sbrooks	/* We have received one or more DHCPOFFER packets.   Currently,
648147072Sbrooks	   the only criterion by which we judge leases is whether or
649147072Sbrooks	   not we get a response when we arp for them. */
650147072Sbrooks	picked = NULL;
651147072Sbrooks	for (lp = ip->client->offered_leases; lp; lp = next) {
652147072Sbrooks		next = lp->next;
653147072Sbrooks
654147072Sbrooks		/* Check to see if we got an ARPREPLY for the address
655147072Sbrooks		   in this particular lease. */
656147072Sbrooks		if (!picked) {
657147072Sbrooks			script_init("ARPCHECK", lp->medium);
658147072Sbrooks			script_write_params("check_", lp);
659147072Sbrooks
660147072Sbrooks			/* If the ARPCHECK code detects another
661147072Sbrooks			   machine using the offered address, it exits
662147072Sbrooks			   nonzero.  We need to send a DHCPDECLINE and
663147072Sbrooks			   toss the lease. */
664147072Sbrooks			if (script_go()) {
665147072Sbrooks				make_decline(ip, lp);
666147072Sbrooks				send_decline(ip);
667147072Sbrooks				goto freeit;
668147072Sbrooks			}
669147072Sbrooks			picked = lp;
670147072Sbrooks			picked->next = NULL;
671147072Sbrooks		} else {
672147072Sbrooksfreeit:
673147072Sbrooks			free_client_lease(lp);
674147072Sbrooks		}
675147072Sbrooks	}
676147072Sbrooks	ip->client->offered_leases = NULL;
677147072Sbrooks
678147072Sbrooks	/* If we just tossed all the leases we were offered, go back
679147072Sbrooks	   to square one. */
680147072Sbrooks	if (!picked) {
681147072Sbrooks		ip->client->state = S_INIT;
682147072Sbrooks		state_init(ip);
683147072Sbrooks		return;
684147072Sbrooks	}
685147072Sbrooks
686147072Sbrooks	/* If it was a BOOTREPLY, we can just take the address right now. */
687147072Sbrooks	if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
688147072Sbrooks		ip->client->new = picked;
689147072Sbrooks
690147072Sbrooks		/* Make up some lease expiry times
691147072Sbrooks		   XXX these should be configurable. */
692147072Sbrooks		ip->client->new->expiry = cur_time + 12000;
693147072Sbrooks		ip->client->new->renewal += cur_time + 8000;
694147072Sbrooks		ip->client->new->rebind += cur_time + 10000;
695147072Sbrooks
696147072Sbrooks		ip->client->state = S_REQUESTING;
697147072Sbrooks
698147072Sbrooks		/* Bind to the address we received. */
699147072Sbrooks		bind_lease(ip);
700147072Sbrooks		return;
701147072Sbrooks	}
702147072Sbrooks
703147072Sbrooks	/* Go to the REQUESTING state. */
704147072Sbrooks	ip->client->destination = iaddr_broadcast;
705147072Sbrooks	ip->client->state = S_REQUESTING;
706147072Sbrooks	ip->client->first_sending = cur_time;
707147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
708147072Sbrooks
709147072Sbrooks	/* Make a DHCPREQUEST packet from the lease we picked. */
710147072Sbrooks	make_request(ip, picked);
711147072Sbrooks	ip->client->xid = ip->client->packet.xid;
712147072Sbrooks
713147072Sbrooks	/* Toss the lease we picked - we'll get it back in a DHCPACK. */
714147072Sbrooks	free_client_lease(picked);
715147072Sbrooks
716147072Sbrooks	/* Add an immediate timeout to send the first DHCPREQUEST packet. */
717147072Sbrooks	send_request(ip);
718147072Sbrooks}
719147072Sbrooks
720147072Sbrooks/* state_requesting is called when we receive a DHCPACK message after
721147072Sbrooks   having sent out one or more DHCPREQUEST packets. */
722147072Sbrooks
723147072Sbrooksvoid
724147072Sbrooksdhcpack(struct packet *packet)
725147072Sbrooks{
726147072Sbrooks	struct interface_info *ip = packet->interface;
727147072Sbrooks	struct client_lease *lease;
728147072Sbrooks
729147072Sbrooks	/* If we're not receptive to an offer right now, or if the offer
730147072Sbrooks	   has an unrecognizable transaction id, then just drop it. */
731147072Sbrooks	if (packet->interface->client->xid != packet->raw->xid ||
732147072Sbrooks	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
733147072Sbrooks	    (memcmp(packet->interface->hw_address.haddr,
734147072Sbrooks	    packet->raw->chaddr, packet->raw->hlen)))
735147072Sbrooks		return;
736147072Sbrooks
737147072Sbrooks	if (ip->client->state != S_REBOOTING &&
738147072Sbrooks	    ip->client->state != S_REQUESTING &&
739147072Sbrooks	    ip->client->state != S_RENEWING &&
740147072Sbrooks	    ip->client->state != S_REBINDING)
741147072Sbrooks		return;
742147072Sbrooks
743147072Sbrooks	note("DHCPACK from %s", piaddr(packet->client_addr));
744147072Sbrooks
745147072Sbrooks	lease = packet_to_lease(packet);
746147072Sbrooks	if (!lease) {
747147072Sbrooks		note("packet_to_lease failed.");
748147072Sbrooks		return;
749147072Sbrooks	}
750147072Sbrooks
751147072Sbrooks	ip->client->new = lease;
752147072Sbrooks
753147072Sbrooks	/* Stop resending DHCPREQUEST. */
754147072Sbrooks	cancel_timeout(send_request, ip);
755147072Sbrooks
756147072Sbrooks	/* Figure out the lease time. */
757147072Sbrooks	if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
758147072Sbrooks		ip->client->new->expiry = getULong(
759147072Sbrooks		    ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
760147072Sbrooks	else
761147072Sbrooks		ip->client->new->expiry = default_lease_time;
762147072Sbrooks	/* A number that looks negative here is really just very large,
763147072Sbrooks	   because the lease expiry offset is unsigned. */
764147072Sbrooks	if (ip->client->new->expiry < 0)
765147072Sbrooks		ip->client->new->expiry = TIME_MAX;
766147072Sbrooks	/* XXX should be fixed by resetting the client state */
767147072Sbrooks	if (ip->client->new->expiry < 60)
768147072Sbrooks		ip->client->new->expiry = 60;
769147072Sbrooks
770147072Sbrooks	/* Take the server-provided renewal time if there is one;
771147072Sbrooks	   otherwise figure it out according to the spec. */
772147072Sbrooks	if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
773147072Sbrooks		ip->client->new->renewal = getULong(
774147072Sbrooks		    ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
775147072Sbrooks	else
776147072Sbrooks		ip->client->new->renewal = ip->client->new->expiry / 2;
777147072Sbrooks
778147072Sbrooks	/* Same deal with the rebind time. */
779147072Sbrooks	if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
780147072Sbrooks		ip->client->new->rebind = getULong(
781147072Sbrooks		    ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
782147072Sbrooks	else
783147072Sbrooks		ip->client->new->rebind = ip->client->new->renewal +
784147072Sbrooks		    ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
785147072Sbrooks
786147072Sbrooks	ip->client->new->expiry += cur_time;
787147072Sbrooks	/* Lease lengths can never be negative. */
788147072Sbrooks	if (ip->client->new->expiry < cur_time)
789147072Sbrooks		ip->client->new->expiry = TIME_MAX;
790147072Sbrooks	ip->client->new->renewal += cur_time;
791147072Sbrooks	if (ip->client->new->renewal < cur_time)
792147072Sbrooks		ip->client->new->renewal = TIME_MAX;
793147072Sbrooks	ip->client->new->rebind += cur_time;
794147072Sbrooks	if (ip->client->new->rebind < cur_time)
795147072Sbrooks		ip->client->new->rebind = TIME_MAX;
796147072Sbrooks
797147072Sbrooks	bind_lease(ip);
798147072Sbrooks}
799147072Sbrooks
800147072Sbrooksvoid
801147072Sbrooksbind_lease(struct interface_info *ip)
802147072Sbrooks{
803147072Sbrooks	/* Remember the medium. */
804147072Sbrooks	ip->client->new->medium = ip->client->medium;
805147072Sbrooks
806147072Sbrooks	/* Write out the new lease. */
807147072Sbrooks	write_client_lease(ip, ip->client->new, 0);
808147072Sbrooks
809147072Sbrooks	/* Run the client script with the new parameters. */
810147072Sbrooks	script_init((ip->client->state == S_REQUESTING ? "BOUND" :
811147072Sbrooks	    (ip->client->state == S_RENEWING ? "RENEW" :
812147072Sbrooks	    (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))),
813147072Sbrooks	    ip->client->new->medium);
814147072Sbrooks	if (ip->client->active && ip->client->state != S_REBOOTING)
815147072Sbrooks		script_write_params("old_", ip->client->active);
816147072Sbrooks	script_write_params("new_", ip->client->new);
817147072Sbrooks	if (ip->client->alias)
818147072Sbrooks		script_write_params("alias_", ip->client->alias);
819147072Sbrooks	script_go();
820147072Sbrooks
821147072Sbrooks	/* Replace the old active lease with the new one. */
822147072Sbrooks	if (ip->client->active)
823147072Sbrooks		free_client_lease(ip->client->active);
824147072Sbrooks	ip->client->active = ip->client->new;
825147072Sbrooks	ip->client->new = NULL;
826147072Sbrooks
827147072Sbrooks	/* Set up a timeout to start the renewal process. */
828147072Sbrooks	add_timeout(ip->client->active->renewal, state_bound, ip);
829147072Sbrooks
830147072Sbrooks	note("bound to %s -- renewal in %d seconds.",
831147072Sbrooks	    piaddr(ip->client->active->address),
832147106Sbrooks	    (int)(ip->client->active->renewal - cur_time));
833147072Sbrooks	ip->client->state = S_BOUND;
834147072Sbrooks	reinitialize_interfaces();
835147072Sbrooks	go_daemon();
836147072Sbrooks}
837147072Sbrooks
838147072Sbrooks/*
839147072Sbrooks * state_bound is called when we've successfully bound to a particular
840147072Sbrooks * lease, but the renewal time on that lease has expired.   We are
841147072Sbrooks * expected to unicast a DHCPREQUEST to the server that gave us our
842147072Sbrooks * original lease.
843147072Sbrooks */
844147072Sbrooksvoid
845147072Sbrooksstate_bound(void *ipp)
846147072Sbrooks{
847147072Sbrooks	struct interface_info *ip = ipp;
848147072Sbrooks
849147072Sbrooks	ASSERT_STATE(state, S_BOUND);
850147072Sbrooks
851147072Sbrooks	/* T1 has expired. */
852147072Sbrooks	make_request(ip, ip->client->active);
853147072Sbrooks	ip->client->xid = ip->client->packet.xid;
854147072Sbrooks
855147072Sbrooks	if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
856147072Sbrooks		memcpy(ip->client->destination.iabuf, ip->client->active->
857147072Sbrooks		    options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
858147072Sbrooks		ip->client->destination.len = 4;
859147072Sbrooks	} else
860147072Sbrooks		ip->client->destination = iaddr_broadcast;
861147072Sbrooks
862147072Sbrooks	ip->client->first_sending = cur_time;
863147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
864147072Sbrooks	ip->client->state = S_RENEWING;
865147072Sbrooks
866147072Sbrooks	/* Send the first packet immediately. */
867147072Sbrooks	send_request(ip);
868147072Sbrooks}
869147072Sbrooks
870147072Sbrooksvoid
871147072Sbrooksbootp(struct packet *packet)
872147072Sbrooks{
873147072Sbrooks	struct iaddrlist *ap;
874147072Sbrooks
875147072Sbrooks	if (packet->raw->op != BOOTREPLY)
876147072Sbrooks		return;
877147072Sbrooks
878147072Sbrooks	/* If there's a reject list, make sure this packet's sender isn't
879147072Sbrooks	   on it. */
880147072Sbrooks	for (ap = packet->interface->client->config->reject_list;
881147072Sbrooks	    ap; ap = ap->next) {
882147072Sbrooks		if (addr_eq(packet->client_addr, ap->addr)) {
883147072Sbrooks			note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
884147072Sbrooks			return;
885147072Sbrooks		}
886147072Sbrooks	}
887147072Sbrooks	dhcpoffer(packet);
888147072Sbrooks}
889147072Sbrooks
890147072Sbrooksvoid
891147072Sbrooksdhcp(struct packet *packet)
892147072Sbrooks{
893147072Sbrooks	struct iaddrlist *ap;
894147072Sbrooks	void (*handler)(struct packet *);
895147072Sbrooks	char *type;
896147072Sbrooks
897147072Sbrooks	switch (packet->packet_type) {
898147072Sbrooks	case DHCPOFFER:
899147072Sbrooks		handler = dhcpoffer;
900147072Sbrooks		type = "DHCPOFFER";
901147072Sbrooks		break;
902147072Sbrooks	case DHCPNAK:
903147072Sbrooks		handler = dhcpnak;
904147072Sbrooks		type = "DHCPNACK";
905147072Sbrooks		break;
906147072Sbrooks	case DHCPACK:
907147072Sbrooks		handler = dhcpack;
908147072Sbrooks		type = "DHCPACK";
909147072Sbrooks		break;
910147072Sbrooks	default:
911147072Sbrooks		return;
912147072Sbrooks	}
913147072Sbrooks
914147072Sbrooks	/* If there's a reject list, make sure this packet's sender isn't
915147072Sbrooks	   on it. */
916147072Sbrooks	for (ap = packet->interface->client->config->reject_list;
917147072Sbrooks	    ap; ap = ap->next) {
918147072Sbrooks		if (addr_eq(packet->client_addr, ap->addr)) {
919147072Sbrooks			note("%s from %s rejected.", type, piaddr(ap->addr));
920147072Sbrooks			return;
921147072Sbrooks		}
922147072Sbrooks	}
923147072Sbrooks	(*handler)(packet);
924147072Sbrooks}
925147072Sbrooks
926147072Sbrooksvoid
927147072Sbrooksdhcpoffer(struct packet *packet)
928147072Sbrooks{
929147072Sbrooks	struct interface_info *ip = packet->interface;
930147072Sbrooks	struct client_lease *lease, *lp;
931147072Sbrooks	int i;
932147072Sbrooks	int arp_timeout_needed, stop_selecting;
933147072Sbrooks	char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
934147072Sbrooks	    "DHCPOFFER" : "BOOTREPLY";
935147072Sbrooks
936147072Sbrooks	/* If we're not receptive to an offer right now, or if the offer
937147072Sbrooks	   has an unrecognizable transaction id, then just drop it. */
938147072Sbrooks	if (ip->client->state != S_SELECTING ||
939147072Sbrooks	    packet->interface->client->xid != packet->raw->xid ||
940147072Sbrooks	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
941147072Sbrooks	    (memcmp(packet->interface->hw_address.haddr,
942147072Sbrooks	    packet->raw->chaddr, packet->raw->hlen)))
943147072Sbrooks		return;
944147072Sbrooks
945147072Sbrooks	note("%s from %s", name, piaddr(packet->client_addr));
946147072Sbrooks
947147072Sbrooks
948147072Sbrooks	/* If this lease doesn't supply the minimum required parameters,
949147072Sbrooks	   blow it off. */
950147072Sbrooks	for (i = 0; ip->client->config->required_options[i]; i++) {
951147072Sbrooks		if (!packet->options[ip->client->config->
952147072Sbrooks		    required_options[i]].len) {
953147072Sbrooks			note("%s isn't satisfactory.", name);
954147072Sbrooks			return;
955147072Sbrooks		}
956147072Sbrooks	}
957147072Sbrooks
958147072Sbrooks	/* If we've already seen this lease, don't record it again. */
959147072Sbrooks	for (lease = ip->client->offered_leases;
960147072Sbrooks	    lease; lease = lease->next) {
961147072Sbrooks		if (lease->address.len == sizeof(packet->raw->yiaddr) &&
962147072Sbrooks		    !memcmp(lease->address.iabuf,
963147072Sbrooks		    &packet->raw->yiaddr, lease->address.len)) {
964147072Sbrooks			debug("%s already seen.", name);
965147072Sbrooks			return;
966147072Sbrooks		}
967147072Sbrooks	}
968147072Sbrooks
969147072Sbrooks	lease = packet_to_lease(packet);
970147072Sbrooks	if (!lease) {
971147072Sbrooks		note("packet_to_lease failed.");
972147072Sbrooks		return;
973147072Sbrooks	}
974147072Sbrooks
975147072Sbrooks	/* If this lease was acquired through a BOOTREPLY, record that
976147072Sbrooks	   fact. */
977147072Sbrooks	if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
978147072Sbrooks		lease->is_bootp = 1;
979147072Sbrooks
980147072Sbrooks	/* Record the medium under which this lease was offered. */
981147072Sbrooks	lease->medium = ip->client->medium;
982147072Sbrooks
983147072Sbrooks	/* Send out an ARP Request for the offered IP address. */
984147072Sbrooks	script_init("ARPSEND", lease->medium);
985147072Sbrooks	script_write_params("check_", lease);
986147072Sbrooks	/* If the script can't send an ARP request without waiting,
987147072Sbrooks	   we'll be waiting when we do the ARPCHECK, so don't wait now. */
988147072Sbrooks	if (script_go())
989147072Sbrooks		arp_timeout_needed = 0;
990147072Sbrooks	else
991147072Sbrooks		arp_timeout_needed = 2;
992147072Sbrooks
993147072Sbrooks	/* Figure out when we're supposed to stop selecting. */
994147072Sbrooks	stop_selecting =
995147072Sbrooks	    ip->client->first_sending + ip->client->config->select_interval;
996147072Sbrooks
997147072Sbrooks	/* If this is the lease we asked for, put it at the head of the
998147072Sbrooks	   list, and don't mess with the arp request timeout. */
999147072Sbrooks	if (lease->address.len == ip->client->requested_address.len &&
1000147072Sbrooks	    !memcmp(lease->address.iabuf,
1001147072Sbrooks	    ip->client->requested_address.iabuf,
1002147072Sbrooks	    ip->client->requested_address.len)) {
1003147072Sbrooks		lease->next = ip->client->offered_leases;
1004147072Sbrooks		ip->client->offered_leases = lease;
1005147072Sbrooks	} else {
1006147072Sbrooks		/* If we already have an offer, and arping for this
1007147072Sbrooks		   offer would take us past the selection timeout,
1008147072Sbrooks		   then don't extend the timeout - just hope for the
1009147072Sbrooks		   best. */
1010147072Sbrooks		if (ip->client->offered_leases &&
1011147072Sbrooks		    (cur_time + arp_timeout_needed) > stop_selecting)
1012147072Sbrooks			arp_timeout_needed = 0;
1013147072Sbrooks
1014147072Sbrooks		/* Put the lease at the end of the list. */
1015147072Sbrooks		lease->next = NULL;
1016147072Sbrooks		if (!ip->client->offered_leases)
1017147072Sbrooks			ip->client->offered_leases = lease;
1018147072Sbrooks		else {
1019147072Sbrooks			for (lp = ip->client->offered_leases; lp->next;
1020147072Sbrooks			    lp = lp->next)
1021147072Sbrooks				;	/* nothing */
1022147072Sbrooks			lp->next = lease;
1023147072Sbrooks		}
1024147072Sbrooks	}
1025147072Sbrooks
1026147072Sbrooks	/* If we're supposed to stop selecting before we've had time
1027147072Sbrooks	   to wait for the ARPREPLY, add some delay to wait for
1028147072Sbrooks	   the ARPREPLY. */
1029147072Sbrooks	if (stop_selecting - cur_time < arp_timeout_needed)
1030147072Sbrooks		stop_selecting = cur_time + arp_timeout_needed;
1031147072Sbrooks
1032147072Sbrooks	/* If the selecting interval has expired, go immediately to
1033147072Sbrooks	   state_selecting().  Otherwise, time out into
1034147072Sbrooks	   state_selecting at the select interval. */
1035147072Sbrooks	if (stop_selecting <= 0)
1036147072Sbrooks		state_selecting(ip);
1037147072Sbrooks	else {
1038147072Sbrooks		add_timeout(stop_selecting, state_selecting, ip);
1039147072Sbrooks		cancel_timeout(send_discover, ip);
1040147072Sbrooks	}
1041147072Sbrooks}
1042147072Sbrooks
1043147072Sbrooks/* Allocate a client_lease structure and initialize it from the parameters
1044147072Sbrooks   in the specified packet. */
1045147072Sbrooks
1046147072Sbrooksstruct client_lease *
1047147072Sbrookspacket_to_lease(struct packet *packet)
1048147072Sbrooks{
1049147072Sbrooks	struct client_lease *lease;
1050147072Sbrooks	int i;
1051147072Sbrooks
1052147072Sbrooks	lease = malloc(sizeof(struct client_lease));
1053147072Sbrooks
1054147072Sbrooks	if (!lease) {
1055147072Sbrooks		warning("dhcpoffer: no memory to record lease.");
1056147072Sbrooks		return (NULL);
1057147072Sbrooks	}
1058147072Sbrooks
1059147072Sbrooks	memset(lease, 0, sizeof(*lease));
1060147072Sbrooks
1061147072Sbrooks	/* Copy the lease options. */
1062147072Sbrooks	for (i = 0; i < 256; i++) {
1063147072Sbrooks		if (packet->options[i].len) {
1064147072Sbrooks			lease->options[i].data =
1065147072Sbrooks			    malloc(packet->options[i].len + 1);
1066147072Sbrooks			if (!lease->options[i].data) {
1067147072Sbrooks				warning("dhcpoffer: no memory for option %d", i);
1068147072Sbrooks				free_client_lease(lease);
1069147072Sbrooks				return (NULL);
1070147072Sbrooks			} else {
1071147072Sbrooks				memcpy(lease->options[i].data,
1072147072Sbrooks				    packet->options[i].data,
1073147072Sbrooks				    packet->options[i].len);
1074147072Sbrooks				lease->options[i].len =
1075147072Sbrooks				    packet->options[i].len;
1076147072Sbrooks				lease->options[i].data[lease->options[i].len] =
1077147072Sbrooks				    0;
1078147072Sbrooks			}
1079147072Sbrooks			if (!check_option(lease,i)) {
1080147072Sbrooks				/* ignore a bogus lease offer */
1081147072Sbrooks				warning("Invalid lease option - ignoring offer");
1082147072Sbrooks				free_client_lease(lease);
1083147072Sbrooks				return (NULL);
1084147072Sbrooks			}
1085147072Sbrooks		}
1086147072Sbrooks	}
1087147072Sbrooks
1088147072Sbrooks	lease->address.len = sizeof(packet->raw->yiaddr);
1089147072Sbrooks	memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
1090147072Sbrooks
1091252506Sbms	lease->nextserver.len = sizeof(packet->raw->siaddr);
1092252506Sbms	memcpy(lease->nextserver.iabuf, &packet->raw->siaddr, lease->nextserver.len);
1093252506Sbms
1094148465Sbrooks	/* If the server name was filled out, copy it.
1095148465Sbrooks	   Do not attempt to validate the server name as a host name.
1096148465Sbrooks	   RFC 2131 merely states that sname is NUL-terminated (which do
1097148465Sbrooks	   do not assume) and that it is the server's host name.  Since
1098148465Sbrooks	   the ISC client and server allow arbitrary characters, we do
1099148465Sbrooks	   as well. */
1100147072Sbrooks	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
1101147072Sbrooks	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
1102147072Sbrooks	    packet->raw->sname[0]) {
1103147072Sbrooks		lease->server_name = malloc(DHCP_SNAME_LEN + 1);
1104147072Sbrooks		if (!lease->server_name) {
1105147072Sbrooks			warning("dhcpoffer: no memory for server name.");
1106147072Sbrooks			free_client_lease(lease);
1107147072Sbrooks			return (NULL);
1108147072Sbrooks		}
1109147072Sbrooks		memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
1110147072Sbrooks		lease->server_name[DHCP_SNAME_LEN]='\0';
1111147072Sbrooks	}
1112147072Sbrooks
1113147072Sbrooks	/* Ditto for the filename. */
1114147072Sbrooks	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
1115147072Sbrooks	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
1116147072Sbrooks	    packet->raw->file[0]) {
1117147072Sbrooks		/* Don't count on the NUL terminator. */
1118147072Sbrooks		lease->filename = malloc(DHCP_FILE_LEN + 1);
1119147072Sbrooks		if (!lease->filename) {
1120147072Sbrooks			warning("dhcpoffer: no memory for filename.");
1121147072Sbrooks			free_client_lease(lease);
1122147072Sbrooks			return (NULL);
1123147072Sbrooks		}
1124147072Sbrooks		memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
1125147072Sbrooks		lease->filename[DHCP_FILE_LEN]='\0';
1126147072Sbrooks	}
1127147072Sbrooks	return lease;
1128147072Sbrooks}
1129147072Sbrooks
1130147072Sbrooksvoid
1131147072Sbrooksdhcpnak(struct packet *packet)
1132147072Sbrooks{
1133147072Sbrooks	struct interface_info *ip = packet->interface;
1134147072Sbrooks
1135147072Sbrooks	/* If we're not receptive to an offer right now, or if the offer
1136147072Sbrooks	   has an unrecognizable transaction id, then just drop it. */
1137147072Sbrooks	if (packet->interface->client->xid != packet->raw->xid ||
1138147072Sbrooks	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
1139147072Sbrooks	    (memcmp(packet->interface->hw_address.haddr,
1140147072Sbrooks	    packet->raw->chaddr, packet->raw->hlen)))
1141147072Sbrooks		return;
1142147072Sbrooks
1143147072Sbrooks	if (ip->client->state != S_REBOOTING &&
1144147072Sbrooks	    ip->client->state != S_REQUESTING &&
1145147072Sbrooks	    ip->client->state != S_RENEWING &&
1146147072Sbrooks	    ip->client->state != S_REBINDING)
1147147072Sbrooks		return;
1148147072Sbrooks
1149147072Sbrooks	note("DHCPNAK from %s", piaddr(packet->client_addr));
1150147072Sbrooks
1151147072Sbrooks	if (!ip->client->active) {
1152147072Sbrooks		note("DHCPNAK with no active lease.\n");
1153147072Sbrooks		return;
1154147072Sbrooks	}
1155147072Sbrooks
1156147072Sbrooks	free_client_lease(ip->client->active);
1157147072Sbrooks	ip->client->active = NULL;
1158147072Sbrooks
1159147072Sbrooks	/* Stop sending DHCPREQUEST packets... */
1160147072Sbrooks	cancel_timeout(send_request, ip);
1161147072Sbrooks
1162147072Sbrooks	ip->client->state = S_INIT;
1163147072Sbrooks	state_init(ip);
1164147072Sbrooks}
1165147072Sbrooks
1166147072Sbrooks/* Send out a DHCPDISCOVER packet, and set a timeout to send out another
1167147072Sbrooks   one after the right interval has expired.  If we don't get an offer by
1168147072Sbrooks   the time we reach the panic interval, call the panic function. */
1169147072Sbrooks
1170147072Sbrooksvoid
1171147072Sbrookssend_discover(void *ipp)
1172147072Sbrooks{
1173147072Sbrooks	struct interface_info *ip = ipp;
1174147072Sbrooks	int interval, increase = 1;
1175147072Sbrooks
1176147072Sbrooks	/* Figure out how long it's been since we started transmitting. */
1177147072Sbrooks	interval = cur_time - ip->client->first_sending;
1178147072Sbrooks
1179147072Sbrooks	/* If we're past the panic timeout, call the script and tell it
1180147072Sbrooks	   we haven't found anything for this interface yet. */
1181147072Sbrooks	if (interval > ip->client->config->timeout) {
1182147072Sbrooks		state_panic(ip);
1183147072Sbrooks		return;
1184147072Sbrooks	}
1185147072Sbrooks
1186147072Sbrooks	/* If we're selecting media, try the whole list before doing
1187147072Sbrooks	   the exponential backoff, but if we've already received an
1188147072Sbrooks	   offer, stop looping, because we obviously have it right. */
1189147072Sbrooks	if (!ip->client->offered_leases &&
1190147072Sbrooks	    ip->client->config->media) {
1191147072Sbrooks		int fail = 0;
1192147072Sbrooksagain:
1193147072Sbrooks		if (ip->client->medium) {
1194147072Sbrooks			ip->client->medium = ip->client->medium->next;
1195147072Sbrooks			increase = 0;
1196147072Sbrooks		}
1197147072Sbrooks		if (!ip->client->medium) {
1198147072Sbrooks			if (fail)
1199147072Sbrooks				error("No valid media types for %s!", ip->name);
1200147072Sbrooks			ip->client->medium = ip->client->config->media;
1201147072Sbrooks			increase = 1;
1202147072Sbrooks		}
1203147072Sbrooks
1204147072Sbrooks		note("Trying medium \"%s\" %d", ip->client->medium->string,
1205147072Sbrooks		    increase);
1206147072Sbrooks		script_init("MEDIUM", ip->client->medium);
1207147072Sbrooks		if (script_go())
1208147072Sbrooks			goto again;
1209147072Sbrooks	}
1210147072Sbrooks
1211147072Sbrooks	/*
1212147072Sbrooks	 * If we're supposed to increase the interval, do so.  If it's
1213147072Sbrooks	 * currently zero (i.e., we haven't sent any packets yet), set
1214147072Sbrooks	 * it to one; otherwise, add to it a random number between zero
1215147072Sbrooks	 * and two times itself.  On average, this means that it will
1216147072Sbrooks	 * double with every transmission.
1217147072Sbrooks	 */
1218147072Sbrooks	if (increase) {
1219147072Sbrooks		if (!ip->client->interval)
1220147072Sbrooks			ip->client->interval =
1221147072Sbrooks			    ip->client->config->initial_interval;
1222147072Sbrooks		else {
1223147072Sbrooks			ip->client->interval += (arc4random() >> 2) %
1224147072Sbrooks			    (2 * ip->client->interval);
1225147072Sbrooks		}
1226147072Sbrooks
1227147072Sbrooks		/* Don't backoff past cutoff. */
1228147072Sbrooks		if (ip->client->interval >
1229147072Sbrooks		    ip->client->config->backoff_cutoff)
1230147072Sbrooks			ip->client->interval =
1231147072Sbrooks				((ip->client->config->backoff_cutoff / 2)
1232147072Sbrooks				 + ((arc4random() >> 2) %
1233147072Sbrooks				    ip->client->config->backoff_cutoff));
1234147072Sbrooks	} else if (!ip->client->interval)
1235147072Sbrooks		ip->client->interval =
1236147072Sbrooks			ip->client->config->initial_interval;
1237147072Sbrooks
1238147072Sbrooks	/* If the backoff would take us to the panic timeout, just use that
1239147072Sbrooks	   as the interval. */
1240147072Sbrooks	if (cur_time + ip->client->interval >
1241147072Sbrooks	    ip->client->first_sending + ip->client->config->timeout)
1242147072Sbrooks		ip->client->interval =
1243147072Sbrooks			(ip->client->first_sending +
1244147072Sbrooks			 ip->client->config->timeout) - cur_time + 1;
1245147072Sbrooks
1246147072Sbrooks	/* Record the number of seconds since we started sending. */
1247147072Sbrooks	if (interval < 65536)
1248147072Sbrooks		ip->client->packet.secs = htons(interval);
1249147072Sbrooks	else
1250147072Sbrooks		ip->client->packet.secs = htons(65535);
1251147072Sbrooks	ip->client->secs = ip->client->packet.secs;
1252147072Sbrooks
1253147072Sbrooks	note("DHCPDISCOVER on %s to %s port %d interval %d",
1254252616Spjd	    ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT,
1255147106Sbrooks	    (int)ip->client->interval);
1256147072Sbrooks
1257147072Sbrooks	/* Send out a packet. */
1258252626Spjd	send_packet_unpriv(privfd, &ip->client->packet,
1259252626Spjd	    ip->client->packet_length, inaddr_any, inaddr_broadcast);
1260147072Sbrooks
1261147072Sbrooks	add_timeout(cur_time + ip->client->interval, send_discover, ip);
1262147072Sbrooks}
1263147072Sbrooks
1264147072Sbrooks/*
1265147072Sbrooks * state_panic gets called if we haven't received any offers in a preset
1266147072Sbrooks * amount of time.   When this happens, we try to use existing leases
1267147072Sbrooks * that haven't yet expired, and failing that, we call the client script
1268147072Sbrooks * and hope it can do something.
1269147072Sbrooks */
1270147072Sbrooksvoid
1271147072Sbrooksstate_panic(void *ipp)
1272147072Sbrooks{
1273147072Sbrooks	struct interface_info *ip = ipp;
1274147072Sbrooks	struct client_lease *loop = ip->client->active;
1275147072Sbrooks	struct client_lease *lp;
1276147072Sbrooks
1277147072Sbrooks	note("No DHCPOFFERS received.");
1278147072Sbrooks
1279147072Sbrooks	/* We may not have an active lease, but we may have some
1280147072Sbrooks	   predefined leases that we can try. */
1281147072Sbrooks	if (!ip->client->active && ip->client->leases)
1282147072Sbrooks		goto activate_next;
1283147072Sbrooks
1284147072Sbrooks	/* Run through the list of leases and see if one can be used. */
1285147072Sbrooks	while (ip->client->active) {
1286147072Sbrooks		if (ip->client->active->expiry > cur_time) {
1287147072Sbrooks			note("Trying recorded lease %s",
1288147072Sbrooks			    piaddr(ip->client->active->address));
1289147072Sbrooks			/* Run the client script with the existing
1290147072Sbrooks			   parameters. */
1291147072Sbrooks			script_init("TIMEOUT",
1292147072Sbrooks			    ip->client->active->medium);
1293147072Sbrooks			script_write_params("new_", ip->client->active);
1294147072Sbrooks			if (ip->client->alias)
1295147072Sbrooks				script_write_params("alias_",
1296147072Sbrooks				    ip->client->alias);
1297147072Sbrooks
1298147072Sbrooks			/* If the old lease is still good and doesn't
1299147072Sbrooks			   yet need renewal, go into BOUND state and
1300147072Sbrooks			   timeout at the renewal time. */
1301147072Sbrooks			if (!script_go()) {
1302147072Sbrooks				if (cur_time <
1303147072Sbrooks				    ip->client->active->renewal) {
1304147072Sbrooks					ip->client->state = S_BOUND;
1305147072Sbrooks					note("bound: renewal in %d seconds.",
1306147106Sbrooks					    (int)(ip->client->active->renewal -
1307147106Sbrooks					    cur_time));
1308147072Sbrooks					add_timeout(
1309147072Sbrooks					    ip->client->active->renewal,
1310147072Sbrooks					    state_bound, ip);
1311147072Sbrooks				} else {
1312147072Sbrooks					ip->client->state = S_BOUND;
1313147072Sbrooks					note("bound: immediate renewal.");
1314147072Sbrooks					state_bound(ip);
1315147072Sbrooks				}
1316147072Sbrooks				reinitialize_interfaces();
1317147072Sbrooks				go_daemon();
1318147072Sbrooks				return;
1319147072Sbrooks			}
1320147072Sbrooks		}
1321147072Sbrooks
1322147072Sbrooks		/* If there are no other leases, give up. */
1323147072Sbrooks		if (!ip->client->leases) {
1324147072Sbrooks			ip->client->leases = ip->client->active;
1325147072Sbrooks			ip->client->active = NULL;
1326147072Sbrooks			break;
1327147072Sbrooks		}
1328147072Sbrooks
1329147072Sbrooksactivate_next:
1330147072Sbrooks		/* Otherwise, put the active lease at the end of the
1331147072Sbrooks		   lease list, and try another lease.. */
1332147072Sbrooks		for (lp = ip->client->leases; lp->next; lp = lp->next)
1333147072Sbrooks			;
1334147072Sbrooks		lp->next = ip->client->active;
1335147072Sbrooks		if (lp->next)
1336147072Sbrooks			lp->next->next = NULL;
1337147072Sbrooks		ip->client->active = ip->client->leases;
1338147072Sbrooks		ip->client->leases = ip->client->leases->next;
1339147072Sbrooks
1340147072Sbrooks		/* If we already tried this lease, we've exhausted the
1341147072Sbrooks		   set of leases, so we might as well give up for
1342147072Sbrooks		   now. */
1343147072Sbrooks		if (ip->client->active == loop)
1344147072Sbrooks			break;
1345147072Sbrooks		else if (!loop)
1346147072Sbrooks			loop = ip->client->active;
1347147072Sbrooks	}
1348147072Sbrooks
1349147072Sbrooks	/* No leases were available, or what was available didn't work, so
1350147072Sbrooks	   tell the shell script that we failed to allocate an address,
1351147072Sbrooks	   and try again later. */
1352147072Sbrooks	note("No working leases in persistent database - sleeping.\n");
1353147072Sbrooks	script_init("FAIL", NULL);
1354147072Sbrooks	if (ip->client->alias)
1355147072Sbrooks		script_write_params("alias_", ip->client->alias);
1356147072Sbrooks	script_go();
1357147072Sbrooks	ip->client->state = S_INIT;
1358147072Sbrooks	add_timeout(cur_time + ip->client->config->retry_interval, state_init,
1359147072Sbrooks	    ip);
1360147072Sbrooks	go_daemon();
1361147072Sbrooks}
1362147072Sbrooks
1363147072Sbrooksvoid
1364147072Sbrookssend_request(void *ipp)
1365147072Sbrooks{
1366147072Sbrooks	struct interface_info *ip = ipp;
1367252616Spjd	struct in_addr from, to;
1368147072Sbrooks	int interval;
1369147072Sbrooks
1370147072Sbrooks	/* Figure out how long it's been since we started transmitting. */
1371147072Sbrooks	interval = cur_time - ip->client->first_sending;
1372147072Sbrooks
1373147072Sbrooks	/* If we're in the INIT-REBOOT or REQUESTING state and we're
1374147072Sbrooks	   past the reboot timeout, go to INIT and see if we can
1375147072Sbrooks	   DISCOVER an address... */
1376147072Sbrooks	/* XXX In the INIT-REBOOT state, if we don't get an ACK, it
1377147072Sbrooks	   means either that we're on a network with no DHCP server,
1378147072Sbrooks	   or that our server is down.  In the latter case, assuming
1379147072Sbrooks	   that there is a backup DHCP server, DHCPDISCOVER will get
1380147072Sbrooks	   us a new address, but we could also have successfully
1381147072Sbrooks	   reused our old address.  In the former case, we're hosed
1382147072Sbrooks	   anyway.  This is not a win-prone situation. */
1383147072Sbrooks	if ((ip->client->state == S_REBOOTING ||
1384147072Sbrooks	    ip->client->state == S_REQUESTING) &&
1385147072Sbrooks	    interval > ip->client->config->reboot_timeout) {
1386147072Sbrookscancel:
1387147072Sbrooks		ip->client->state = S_INIT;
1388147072Sbrooks		cancel_timeout(send_request, ip);
1389147072Sbrooks		state_init(ip);
1390147072Sbrooks		return;
1391147072Sbrooks	}
1392147072Sbrooks
1393147072Sbrooks	/* If we're in the reboot state, make sure the media is set up
1394147072Sbrooks	   correctly. */
1395147072Sbrooks	if (ip->client->state == S_REBOOTING &&
1396147072Sbrooks	    !ip->client->medium &&
1397147072Sbrooks	    ip->client->active->medium ) {
1398147072Sbrooks		script_init("MEDIUM", ip->client->active->medium);
1399147072Sbrooks
1400147072Sbrooks		/* If the medium we chose won't fly, go to INIT state. */
1401147072Sbrooks		if (script_go())
1402147072Sbrooks			goto cancel;
1403147072Sbrooks
1404147072Sbrooks		/* Record the medium. */
1405147072Sbrooks		ip->client->medium = ip->client->active->medium;
1406147072Sbrooks	}
1407147072Sbrooks
1408147072Sbrooks	/* If the lease has expired, relinquish the address and go back
1409147072Sbrooks	   to the INIT state. */
1410147072Sbrooks	if (ip->client->state != S_REQUESTING &&
1411147072Sbrooks	    cur_time > ip->client->active->expiry) {
1412147072Sbrooks		/* Run the client script with the new parameters. */
1413147072Sbrooks		script_init("EXPIRE", NULL);
1414147072Sbrooks		script_write_params("old_", ip->client->active);
1415147072Sbrooks		if (ip->client->alias)
1416147072Sbrooks			script_write_params("alias_", ip->client->alias);
1417147072Sbrooks		script_go();
1418147072Sbrooks
1419147072Sbrooks		/* Now do a preinit on the interface so that we can
1420147072Sbrooks		   discover a new address. */
1421147072Sbrooks		script_init("PREINIT", NULL);
1422147072Sbrooks		if (ip->client->alias)
1423147072Sbrooks			script_write_params("alias_", ip->client->alias);
1424147072Sbrooks		script_go();
1425147072Sbrooks
1426147072Sbrooks		ip->client->state = S_INIT;
1427147072Sbrooks		state_init(ip);
1428147072Sbrooks		return;
1429147072Sbrooks	}
1430147072Sbrooks
1431147072Sbrooks	/* Do the exponential backoff... */
1432147072Sbrooks	if (!ip->client->interval)
1433147072Sbrooks		ip->client->interval = ip->client->config->initial_interval;
1434147072Sbrooks	else
1435147072Sbrooks		ip->client->interval += ((arc4random() >> 2) %
1436147072Sbrooks		    (2 * ip->client->interval));
1437147072Sbrooks
1438147072Sbrooks	/* Don't backoff past cutoff. */
1439147072Sbrooks	if (ip->client->interval >
1440147072Sbrooks	    ip->client->config->backoff_cutoff)
1441147072Sbrooks		ip->client->interval =
1442147072Sbrooks		    ((ip->client->config->backoff_cutoff / 2) +
1443147072Sbrooks		    ((arc4random() >> 2) % ip->client->interval));
1444147072Sbrooks
1445147072Sbrooks	/* If the backoff would take us to the expiry time, just set the
1446147072Sbrooks	   timeout to the expiry time. */
1447147072Sbrooks	if (ip->client->state != S_REQUESTING &&
1448147072Sbrooks	    cur_time + ip->client->interval >
1449147072Sbrooks	    ip->client->active->expiry)
1450147072Sbrooks		ip->client->interval =
1451147072Sbrooks		    ip->client->active->expiry - cur_time + 1;
1452147072Sbrooks
1453147072Sbrooks	/* If the lease T2 time has elapsed, or if we're not yet bound,
1454147072Sbrooks	   broadcast the DHCPREQUEST rather than unicasting. */
1455147072Sbrooks	if (ip->client->state == S_REQUESTING ||
1456147072Sbrooks	    ip->client->state == S_REBOOTING ||
1457147072Sbrooks	    cur_time > ip->client->active->rebind)
1458252616Spjd		to.s_addr = INADDR_BROADCAST;
1459147072Sbrooks	else
1460252616Spjd		memcpy(&to.s_addr, ip->client->destination.iabuf,
1461252616Spjd		    sizeof(to.s_addr));
1462147072Sbrooks
1463330693Sdab	if (ip->client->state != S_REQUESTING &&
1464330693Sdab	    ip->client->state != S_REBOOTING)
1465147072Sbrooks		memcpy(&from, ip->client->active->address.iabuf,
1466147072Sbrooks		    sizeof(from));
1467147072Sbrooks	else
1468147072Sbrooks		from.s_addr = INADDR_ANY;
1469147072Sbrooks
1470147072Sbrooks	/* Record the number of seconds since we started sending. */
1471147072Sbrooks	if (ip->client->state == S_REQUESTING)
1472147072Sbrooks		ip->client->packet.secs = ip->client->secs;
1473147072Sbrooks	else {
1474147072Sbrooks		if (interval < 65536)
1475147072Sbrooks			ip->client->packet.secs = htons(interval);
1476147072Sbrooks		else
1477147072Sbrooks			ip->client->packet.secs = htons(65535);
1478147072Sbrooks	}
1479147072Sbrooks
1480252616Spjd	note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to),
1481252616Spjd	    REMOTE_PORT);
1482147072Sbrooks
1483147072Sbrooks	/* Send out a packet. */
1484252626Spjd	send_packet_unpriv(privfd, &ip->client->packet,
1485252626Spjd	    ip->client->packet_length, from, to);
1486147072Sbrooks
1487147072Sbrooks	add_timeout(cur_time + ip->client->interval, send_request, ip);
1488147072Sbrooks}
1489147072Sbrooks
1490147072Sbrooksvoid
1491147072Sbrookssend_decline(void *ipp)
1492147072Sbrooks{
1493147072Sbrooks	struct interface_info *ip = ipp;
1494147072Sbrooks
1495147072Sbrooks	note("DHCPDECLINE on %s to %s port %d", ip->name,
1496252616Spjd	    inet_ntoa(inaddr_broadcast), REMOTE_PORT);
1497147072Sbrooks
1498147072Sbrooks	/* Send out a packet. */
1499252626Spjd	send_packet_unpriv(privfd, &ip->client->packet,
1500252626Spjd	    ip->client->packet_length, inaddr_any, inaddr_broadcast);
1501147072Sbrooks}
1502147072Sbrooks
1503147072Sbrooksvoid
1504147072Sbrooksmake_discover(struct interface_info *ip, struct client_lease *lease)
1505147072Sbrooks{
1506147072Sbrooks	unsigned char discover = DHCPDISCOVER;
1507147072Sbrooks	struct tree_cache *options[256];
1508147072Sbrooks	struct tree_cache option_elements[256];
1509147072Sbrooks	int i;
1510147072Sbrooks
1511147072Sbrooks	memset(option_elements, 0, sizeof(option_elements));
1512147072Sbrooks	memset(options, 0, sizeof(options));
1513147072Sbrooks	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1514147072Sbrooks
1515147072Sbrooks	/* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
1516147072Sbrooks	i = DHO_DHCP_MESSAGE_TYPE;
1517147072Sbrooks	options[i] = &option_elements[i];
1518147072Sbrooks	options[i]->value = &discover;
1519147072Sbrooks	options[i]->len = sizeof(discover);
1520147072Sbrooks	options[i]->buf_size = sizeof(discover);
1521147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1522147072Sbrooks
1523147072Sbrooks	/* Request the options we want */
1524147072Sbrooks	i  = DHO_DHCP_PARAMETER_REQUEST_LIST;
1525147072Sbrooks	options[i] = &option_elements[i];
1526147072Sbrooks	options[i]->value = ip->client->config->requested_options;
1527147072Sbrooks	options[i]->len = ip->client->config->requested_option_count;
1528147072Sbrooks	options[i]->buf_size =
1529147072Sbrooks		ip->client->config->requested_option_count;
1530147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1531147072Sbrooks
1532147072Sbrooks	/* If we had an address, try to get it again. */
1533147072Sbrooks	if (lease) {
1534147072Sbrooks		ip->client->requested_address = lease->address;
1535147072Sbrooks		i = DHO_DHCP_REQUESTED_ADDRESS;
1536147072Sbrooks		options[i] = &option_elements[i];
1537147072Sbrooks		options[i]->value = lease->address.iabuf;
1538147072Sbrooks		options[i]->len = lease->address.len;
1539147072Sbrooks		options[i]->buf_size = lease->address.len;
1540147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1541147072Sbrooks	} else
1542147072Sbrooks		ip->client->requested_address.len = 0;
1543147072Sbrooks
1544147072Sbrooks	/* Send any options requested in the config file. */
1545147072Sbrooks	for (i = 0; i < 256; i++)
1546147072Sbrooks		if (!options[i] &&
1547147072Sbrooks		    ip->client->config->send_options[i].data) {
1548147072Sbrooks			options[i] = &option_elements[i];
1549147072Sbrooks			options[i]->value =
1550147072Sbrooks			    ip->client->config->send_options[i].data;
1551147072Sbrooks			options[i]->len =
1552147072Sbrooks			    ip->client->config->send_options[i].len;
1553147072Sbrooks			options[i]->buf_size =
1554147072Sbrooks			    ip->client->config->send_options[i].len;
1555147072Sbrooks			options[i]->timeout = 0xFFFFFFFF;
1556147072Sbrooks		}
1557252621Spjd
1558158353Sbrooks	/* send host name if not set via config file. */
1559158353Sbrooks	if (!options[DHO_HOST_NAME]) {
1560252623Spjd		if (hostname[0] != '\0') {
1561158353Sbrooks			size_t len;
1562158353Sbrooks			char* posDot = strchr(hostname, '.');
1563158353Sbrooks			if (posDot != NULL)
1564158353Sbrooks				len = posDot - hostname;
1565158353Sbrooks			else
1566158353Sbrooks				len = strlen(hostname);
1567158353Sbrooks			options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME];
1568158353Sbrooks			options[DHO_HOST_NAME]->value = hostname;
1569158353Sbrooks			options[DHO_HOST_NAME]->len = len;
1570158353Sbrooks			options[DHO_HOST_NAME]->buf_size = len;
1571158353Sbrooks			options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF;
1572158353Sbrooks		}
1573158353Sbrooks	}
1574147072Sbrooks
1575158353Sbrooks	/* set unique client identifier */
1576158353Sbrooks	char client_ident[sizeof(struct hardware)];
1577158353Sbrooks	if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
1578158353Sbrooks		int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
1579158353Sbrooks				ip->hw_address.hlen : sizeof(client_ident)-1;
1580158353Sbrooks		client_ident[0] = ip->hw_address.htype;
1581252621Spjd		memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
1582158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
1583158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
1584158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
1585158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1;
1586158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF;
1587158353Sbrooks	}
1588158353Sbrooks
1589147072Sbrooks	/* Set up the option buffer... */
1590147072Sbrooks	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1591147072Sbrooks	    options, 0, 0, 0, NULL, 0);
1592147072Sbrooks	if (ip->client->packet_length < BOOTP_MIN_LEN)
1593147072Sbrooks		ip->client->packet_length = BOOTP_MIN_LEN;
1594147072Sbrooks
1595147072Sbrooks	ip->client->packet.op = BOOTREQUEST;
1596147072Sbrooks	ip->client->packet.htype = ip->hw_address.htype;
1597147072Sbrooks	ip->client->packet.hlen = ip->hw_address.hlen;
1598147072Sbrooks	ip->client->packet.hops = 0;
1599147072Sbrooks	ip->client->packet.xid = arc4random();
1600147072Sbrooks	ip->client->packet.secs = 0; /* filled in by send_discover. */
1601147072Sbrooks	ip->client->packet.flags = 0;
1602147072Sbrooks
1603147072Sbrooks	memset(&(ip->client->packet.ciaddr),
1604147072Sbrooks	    0, sizeof(ip->client->packet.ciaddr));
1605147072Sbrooks	memset(&(ip->client->packet.yiaddr),
1606147072Sbrooks	    0, sizeof(ip->client->packet.yiaddr));
1607147072Sbrooks	memset(&(ip->client->packet.siaddr),
1608147072Sbrooks	    0, sizeof(ip->client->packet.siaddr));
1609147072Sbrooks	memset(&(ip->client->packet.giaddr),
1610147072Sbrooks	    0, sizeof(ip->client->packet.giaddr));
1611147072Sbrooks	memcpy(ip->client->packet.chaddr,
1612147072Sbrooks	    ip->hw_address.haddr, ip->hw_address.hlen);
1613147072Sbrooks}
1614147072Sbrooks
1615147072Sbrooks
1616147072Sbrooksvoid
1617147072Sbrooksmake_request(struct interface_info *ip, struct client_lease * lease)
1618147072Sbrooks{
1619147072Sbrooks	unsigned char request = DHCPREQUEST;
1620147072Sbrooks	struct tree_cache *options[256];
1621147072Sbrooks	struct tree_cache option_elements[256];
1622147072Sbrooks	int i;
1623147072Sbrooks
1624147072Sbrooks	memset(options, 0, sizeof(options));
1625147072Sbrooks	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1626147072Sbrooks
1627147072Sbrooks	/* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
1628147072Sbrooks	i = DHO_DHCP_MESSAGE_TYPE;
1629147072Sbrooks	options[i] = &option_elements[i];
1630147072Sbrooks	options[i]->value = &request;
1631147072Sbrooks	options[i]->len = sizeof(request);
1632147072Sbrooks	options[i]->buf_size = sizeof(request);
1633147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1634147072Sbrooks
1635147072Sbrooks	/* Request the options we want */
1636147072Sbrooks	i = DHO_DHCP_PARAMETER_REQUEST_LIST;
1637147072Sbrooks	options[i] = &option_elements[i];
1638147072Sbrooks	options[i]->value = ip->client->config->requested_options;
1639147072Sbrooks	options[i]->len = ip->client->config->requested_option_count;
1640147072Sbrooks	options[i]->buf_size =
1641147072Sbrooks		ip->client->config->requested_option_count;
1642147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1643147072Sbrooks
1644147072Sbrooks	/* If we are requesting an address that hasn't yet been assigned
1645147072Sbrooks	   to us, use the DHCP Requested Address option. */
1646147072Sbrooks	if (ip->client->state == S_REQUESTING) {
1647147072Sbrooks		/* Send back the server identifier... */
1648147072Sbrooks		i = DHO_DHCP_SERVER_IDENTIFIER;
1649147072Sbrooks		options[i] = &option_elements[i];
1650147072Sbrooks		options[i]->value = lease->options[i].data;
1651147072Sbrooks		options[i]->len = lease->options[i].len;
1652147072Sbrooks		options[i]->buf_size = lease->options[i].len;
1653147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1654147072Sbrooks	}
1655147072Sbrooks	if (ip->client->state == S_REQUESTING ||
1656147072Sbrooks	    ip->client->state == S_REBOOTING) {
1657147072Sbrooks		ip->client->requested_address = lease->address;
1658147072Sbrooks		i = DHO_DHCP_REQUESTED_ADDRESS;
1659147072Sbrooks		options[i] = &option_elements[i];
1660147072Sbrooks		options[i]->value = lease->address.iabuf;
1661147072Sbrooks		options[i]->len = lease->address.len;
1662147072Sbrooks		options[i]->buf_size = lease->address.len;
1663147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1664147072Sbrooks	} else
1665147072Sbrooks		ip->client->requested_address.len = 0;
1666147072Sbrooks
1667147072Sbrooks	/* Send any options requested in the config file. */
1668147072Sbrooks	for (i = 0; i < 256; i++)
1669147072Sbrooks		if (!options[i] &&
1670147072Sbrooks		    ip->client->config->send_options[i].data) {
1671147072Sbrooks			options[i] = &option_elements[i];
1672147072Sbrooks			options[i]->value =
1673147072Sbrooks			    ip->client->config->send_options[i].data;
1674147072Sbrooks			options[i]->len =
1675147072Sbrooks			    ip->client->config->send_options[i].len;
1676147072Sbrooks			options[i]->buf_size =
1677147072Sbrooks			    ip->client->config->send_options[i].len;
1678147072Sbrooks			options[i]->timeout = 0xFFFFFFFF;
1679147072Sbrooks		}
1680252621Spjd
1681158353Sbrooks	/* send host name if not set via config file. */
1682158353Sbrooks	if (!options[DHO_HOST_NAME]) {
1683252623Spjd		if (hostname[0] != '\0') {
1684158353Sbrooks			size_t len;
1685158353Sbrooks			char* posDot = strchr(hostname, '.');
1686158353Sbrooks			if (posDot != NULL)
1687158353Sbrooks				len = posDot - hostname;
1688158353Sbrooks			else
1689158353Sbrooks				len = strlen(hostname);
1690158353Sbrooks			options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME];
1691158353Sbrooks			options[DHO_HOST_NAME]->value = hostname;
1692158353Sbrooks			options[DHO_HOST_NAME]->len = len;
1693158353Sbrooks			options[DHO_HOST_NAME]->buf_size = len;
1694158353Sbrooks			options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF;
1695158353Sbrooks		}
1696158353Sbrooks	}
1697147072Sbrooks
1698158353Sbrooks	/* set unique client identifier */
1699158353Sbrooks	char client_ident[sizeof(struct hardware)];
1700158353Sbrooks	if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
1701158353Sbrooks		int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
1702158353Sbrooks				ip->hw_address.hlen : sizeof(client_ident)-1;
1703158353Sbrooks		client_ident[0] = ip->hw_address.htype;
1704252621Spjd		memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
1705158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
1706158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
1707158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
1708158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1;
1709158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF;
1710158353Sbrooks	}
1711158353Sbrooks
1712147072Sbrooks	/* Set up the option buffer... */
1713147072Sbrooks	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1714147072Sbrooks	    options, 0, 0, 0, NULL, 0);
1715147072Sbrooks	if (ip->client->packet_length < BOOTP_MIN_LEN)
1716147072Sbrooks		ip->client->packet_length = BOOTP_MIN_LEN;
1717147072Sbrooks
1718147072Sbrooks	ip->client->packet.op = BOOTREQUEST;
1719147072Sbrooks	ip->client->packet.htype = ip->hw_address.htype;
1720147072Sbrooks	ip->client->packet.hlen = ip->hw_address.hlen;
1721147072Sbrooks	ip->client->packet.hops = 0;
1722147072Sbrooks	ip->client->packet.xid = ip->client->xid;
1723147072Sbrooks	ip->client->packet.secs = 0; /* Filled in by send_request. */
1724147072Sbrooks
1725147072Sbrooks	/* If we own the address we're requesting, put it in ciaddr;
1726147072Sbrooks	   otherwise set ciaddr to zero. */
1727147072Sbrooks	if (ip->client->state == S_BOUND ||
1728147072Sbrooks	    ip->client->state == S_RENEWING ||
1729147072Sbrooks	    ip->client->state == S_REBINDING) {
1730147072Sbrooks		memcpy(&ip->client->packet.ciaddr,
1731147072Sbrooks		    lease->address.iabuf, lease->address.len);
1732147072Sbrooks		ip->client->packet.flags = 0;
1733147072Sbrooks	} else {
1734147072Sbrooks		memset(&ip->client->packet.ciaddr, 0,
1735147072Sbrooks		    sizeof(ip->client->packet.ciaddr));
1736147072Sbrooks		ip->client->packet.flags = 0;
1737147072Sbrooks	}
1738147072Sbrooks
1739147072Sbrooks	memset(&ip->client->packet.yiaddr, 0,
1740147072Sbrooks	    sizeof(ip->client->packet.yiaddr));
1741147072Sbrooks	memset(&ip->client->packet.siaddr, 0,
1742147072Sbrooks	    sizeof(ip->client->packet.siaddr));
1743147072Sbrooks	memset(&ip->client->packet.giaddr, 0,
1744147072Sbrooks	    sizeof(ip->client->packet.giaddr));
1745147072Sbrooks	memcpy(ip->client->packet.chaddr,
1746147072Sbrooks	    ip->hw_address.haddr, ip->hw_address.hlen);
1747147072Sbrooks}
1748147072Sbrooks
1749147072Sbrooksvoid
1750147072Sbrooksmake_decline(struct interface_info *ip, struct client_lease *lease)
1751147072Sbrooks{
1752147072Sbrooks	struct tree_cache *options[256], message_type_tree;
1753147072Sbrooks	struct tree_cache requested_address_tree;
1754147072Sbrooks	struct tree_cache server_id_tree, client_id_tree;
1755147072Sbrooks	unsigned char decline = DHCPDECLINE;
1756147072Sbrooks	int i;
1757147072Sbrooks
1758147072Sbrooks	memset(options, 0, sizeof(options));
1759147072Sbrooks	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1760147072Sbrooks
1761147072Sbrooks	/* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
1762147072Sbrooks	i = DHO_DHCP_MESSAGE_TYPE;
1763147072Sbrooks	options[i] = &message_type_tree;
1764147072Sbrooks	options[i]->value = &decline;
1765147072Sbrooks	options[i]->len = sizeof(decline);
1766147072Sbrooks	options[i]->buf_size = sizeof(decline);
1767147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1768147072Sbrooks
1769147072Sbrooks	/* Send back the server identifier... */
1770147072Sbrooks	i = DHO_DHCP_SERVER_IDENTIFIER;
1771147072Sbrooks	options[i] = &server_id_tree;
1772147072Sbrooks	options[i]->value = lease->options[i].data;
1773147072Sbrooks	options[i]->len = lease->options[i].len;
1774147072Sbrooks	options[i]->buf_size = lease->options[i].len;
1775147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1776147072Sbrooks
1777147072Sbrooks	/* Send back the address we're declining. */
1778147072Sbrooks	i = DHO_DHCP_REQUESTED_ADDRESS;
1779147072Sbrooks	options[i] = &requested_address_tree;
1780147072Sbrooks	options[i]->value = lease->address.iabuf;
1781147072Sbrooks	options[i]->len = lease->address.len;
1782147072Sbrooks	options[i]->buf_size = lease->address.len;
1783147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1784147072Sbrooks
1785147072Sbrooks	/* Send the uid if the user supplied one. */
1786147072Sbrooks	i = DHO_DHCP_CLIENT_IDENTIFIER;
1787147072Sbrooks	if (ip->client->config->send_options[i].len) {
1788147072Sbrooks		options[i] = &client_id_tree;
1789147072Sbrooks		options[i]->value = ip->client->config->send_options[i].data;
1790147072Sbrooks		options[i]->len = ip->client->config->send_options[i].len;
1791147072Sbrooks		options[i]->buf_size = ip->client->config->send_options[i].len;
1792147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1793147072Sbrooks	}
1794147072Sbrooks
1795147072Sbrooks
1796147072Sbrooks	/* Set up the option buffer... */
1797147072Sbrooks	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1798147072Sbrooks	    options, 0, 0, 0, NULL, 0);
1799147072Sbrooks	if (ip->client->packet_length < BOOTP_MIN_LEN)
1800147072Sbrooks		ip->client->packet_length = BOOTP_MIN_LEN;
1801147072Sbrooks
1802147072Sbrooks	ip->client->packet.op = BOOTREQUEST;
1803147072Sbrooks	ip->client->packet.htype = ip->hw_address.htype;
1804147072Sbrooks	ip->client->packet.hlen = ip->hw_address.hlen;
1805147072Sbrooks	ip->client->packet.hops = 0;
1806147072Sbrooks	ip->client->packet.xid = ip->client->xid;
1807147072Sbrooks	ip->client->packet.secs = 0; /* Filled in by send_request. */
1808147072Sbrooks	ip->client->packet.flags = 0;
1809147072Sbrooks
1810147072Sbrooks	/* ciaddr must always be zero. */
1811147072Sbrooks	memset(&ip->client->packet.ciaddr, 0,
1812147072Sbrooks	    sizeof(ip->client->packet.ciaddr));
1813147072Sbrooks	memset(&ip->client->packet.yiaddr, 0,
1814147072Sbrooks	    sizeof(ip->client->packet.yiaddr));
1815147072Sbrooks	memset(&ip->client->packet.siaddr, 0,
1816147072Sbrooks	    sizeof(ip->client->packet.siaddr));
1817147072Sbrooks	memset(&ip->client->packet.giaddr, 0,
1818147072Sbrooks	    sizeof(ip->client->packet.giaddr));
1819147072Sbrooks	memcpy(ip->client->packet.chaddr,
1820147072Sbrooks	    ip->hw_address.haddr, ip->hw_address.hlen);
1821147072Sbrooks}
1822147072Sbrooks
1823147072Sbrooksvoid
1824147072Sbrooksfree_client_lease(struct client_lease *lease)
1825147072Sbrooks{
1826147072Sbrooks	int i;
1827147072Sbrooks
1828147072Sbrooks	if (lease->server_name)
1829147072Sbrooks		free(lease->server_name);
1830147072Sbrooks	if (lease->filename)
1831147072Sbrooks		free(lease->filename);
1832147072Sbrooks	for (i = 0; i < 256; i++) {
1833147072Sbrooks		if (lease->options[i].len)
1834147072Sbrooks			free(lease->options[i].data);
1835147072Sbrooks	}
1836147072Sbrooks	free(lease);
1837147072Sbrooks}
1838147072Sbrooks
1839147072SbrooksFILE *leaseFile;
1840147072Sbrooks
1841147072Sbrooksvoid
1842147072Sbrooksrewrite_client_leases(void)
1843147072Sbrooks{
1844147072Sbrooks	struct client_lease *lp;
1845255219Spjd	cap_rights_t rights;
1846147072Sbrooks
1847147072Sbrooks	if (!leaseFile) {
1848147072Sbrooks		leaseFile = fopen(path_dhclient_db, "w");
1849147072Sbrooks		if (!leaseFile)
1850147072Sbrooks			error("can't create %s: %m", path_dhclient_db);
1851283978Spkelsey		cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_FSYNC,
1852283978Spkelsey		    CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE);
1853255219Spjd		if (cap_rights_limit(fileno(leaseFile), &rights) < 0 &&
1854252631Spjd		    errno != ENOSYS) {
1855252631Spjd			error("can't limit lease descriptor: %m");
1856252631Spjd		}
1857283978Spkelsey		if (cap_fcntls_limit(fileno(leaseFile), CAP_FCNTL_GETFL) < 0 &&
1858283978Spkelsey		    errno != ENOSYS) {
1859283978Spkelsey			error("can't limit lease descriptor fcntls: %m");
1860283978Spkelsey		}
1861147072Sbrooks	} else {
1862147072Sbrooks		fflush(leaseFile);
1863147072Sbrooks		rewind(leaseFile);
1864147072Sbrooks	}
1865147072Sbrooks
1866147072Sbrooks	for (lp = ifi->client->leases; lp; lp = lp->next)
1867147072Sbrooks		write_client_lease(ifi, lp, 1);
1868147072Sbrooks	if (ifi->client->active)
1869147072Sbrooks		write_client_lease(ifi, ifi->client->active, 1);
1870147072Sbrooks
1871147072Sbrooks	fflush(leaseFile);
1872147072Sbrooks	ftruncate(fileno(leaseFile), ftello(leaseFile));
1873147072Sbrooks	fsync(fileno(leaseFile));
1874147072Sbrooks}
1875147072Sbrooks
1876147072Sbrooksvoid
1877147072Sbrookswrite_client_lease(struct interface_info *ip, struct client_lease *lease,
1878147072Sbrooks    int rewrite)
1879147072Sbrooks{
1880147072Sbrooks	static int leases_written;
1881147072Sbrooks	struct tm *t;
1882147072Sbrooks	int i;
1883147072Sbrooks
1884147072Sbrooks	if (!rewrite) {
1885147072Sbrooks		if (leases_written++ > 20) {
1886147072Sbrooks			rewrite_client_leases();
1887147072Sbrooks			leases_written = 0;
1888147072Sbrooks		}
1889147072Sbrooks	}
1890147072Sbrooks
1891147072Sbrooks	/* If the lease came from the config file, we don't need to stash
1892147072Sbrooks	   a copy in the lease database. */
1893147072Sbrooks	if (lease->is_static)
1894147072Sbrooks		return;
1895147072Sbrooks
1896147072Sbrooks	if (!leaseFile) {	/* XXX */
1897147072Sbrooks		leaseFile = fopen(path_dhclient_db, "w");
1898147072Sbrooks		if (!leaseFile)
1899147072Sbrooks			error("can't create %s: %m", path_dhclient_db);
1900147072Sbrooks	}
1901147072Sbrooks
1902147072Sbrooks	fprintf(leaseFile, "lease {\n");
1903147072Sbrooks	if (lease->is_bootp)
1904147072Sbrooks		fprintf(leaseFile, "  bootp;\n");
1905147072Sbrooks	fprintf(leaseFile, "  interface \"%s\";\n", ip->name);
1906147072Sbrooks	fprintf(leaseFile, "  fixed-address %s;\n", piaddr(lease->address));
1907252506Sbms	if (lease->nextserver.len == sizeof(inaddr_any) &&
1908252506Sbms	    0 != memcmp(lease->nextserver.iabuf, &inaddr_any,
1909252506Sbms	    sizeof(inaddr_any)))
1910252506Sbms		fprintf(leaseFile, "  next-server %s;\n",
1911252506Sbms		    piaddr(lease->nextserver));
1912147072Sbrooks	if (lease->filename)
1913147072Sbrooks		fprintf(leaseFile, "  filename \"%s\";\n", lease->filename);
1914147072Sbrooks	if (lease->server_name)
1915147072Sbrooks		fprintf(leaseFile, "  server-name \"%s\";\n",
1916147072Sbrooks		    lease->server_name);
1917147072Sbrooks	if (lease->medium)
1918147072Sbrooks		fprintf(leaseFile, "  medium \"%s\";\n", lease->medium->string);
1919147072Sbrooks	for (i = 0; i < 256; i++)
1920147072Sbrooks		if (lease->options[i].len)
1921147072Sbrooks			fprintf(leaseFile, "  option %s %s;\n",
1922147072Sbrooks			    dhcp_options[i].name,
1923147072Sbrooks			    pretty_print_option(i, lease->options[i].data,
1924147072Sbrooks			    lease->options[i].len, 1, 1));
1925147072Sbrooks
1926147072Sbrooks	t = gmtime(&lease->renewal);
1927147072Sbrooks	fprintf(leaseFile, "  renew %d %d/%d/%d %02d:%02d:%02d;\n",
1928147072Sbrooks	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1929147072Sbrooks	    t->tm_hour, t->tm_min, t->tm_sec);
1930147072Sbrooks	t = gmtime(&lease->rebind);
1931147072Sbrooks	fprintf(leaseFile, "  rebind %d %d/%d/%d %02d:%02d:%02d;\n",
1932147072Sbrooks	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1933147072Sbrooks	    t->tm_hour, t->tm_min, t->tm_sec);
1934147072Sbrooks	t = gmtime(&lease->expiry);
1935147072Sbrooks	fprintf(leaseFile, "  expire %d %d/%d/%d %02d:%02d:%02d;\n",
1936147072Sbrooks	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1937147072Sbrooks	    t->tm_hour, t->tm_min, t->tm_sec);
1938147072Sbrooks	fprintf(leaseFile, "}\n");
1939147072Sbrooks	fflush(leaseFile);
1940147072Sbrooks}
1941147072Sbrooks
1942147072Sbrooksvoid
1943147072Sbrooksscript_init(char *reason, struct string_list *medium)
1944147072Sbrooks{
1945147072Sbrooks	size_t		 len, mediumlen = 0;
1946147072Sbrooks	struct imsg_hdr	 hdr;
1947147072Sbrooks	struct buf	*buf;
1948147072Sbrooks	int		 errs;
1949147072Sbrooks
1950147072Sbrooks	if (medium != NULL && medium->string != NULL)
1951147072Sbrooks		mediumlen = strlen(medium->string);
1952147072Sbrooks
1953147072Sbrooks	hdr.code = IMSG_SCRIPT_INIT;
1954147072Sbrooks	hdr.len = sizeof(struct imsg_hdr) +
1955147072Sbrooks	    sizeof(size_t) + mediumlen +
1956147072Sbrooks	    sizeof(size_t) + strlen(reason);
1957147072Sbrooks
1958147072Sbrooks	if ((buf = buf_open(hdr.len)) == NULL)
1959147072Sbrooks		error("buf_open: %m");
1960147072Sbrooks
1961147072Sbrooks	errs = 0;
1962147072Sbrooks	errs += buf_add(buf, &hdr, sizeof(hdr));
1963147072Sbrooks	errs += buf_add(buf, &mediumlen, sizeof(mediumlen));
1964147072Sbrooks	if (mediumlen > 0)
1965147072Sbrooks		errs += buf_add(buf, medium->string, mediumlen);
1966147072Sbrooks	len = strlen(reason);
1967147072Sbrooks	errs += buf_add(buf, &len, sizeof(len));
1968147072Sbrooks	errs += buf_add(buf, reason, len);
1969147072Sbrooks
1970147072Sbrooks	if (errs)
1971147072Sbrooks		error("buf_add: %m");
1972147072Sbrooks
1973147072Sbrooks	if (buf_close(privfd, buf) == -1)
1974147072Sbrooks		error("buf_close: %m");
1975147072Sbrooks}
1976147072Sbrooks
1977147072Sbrooksvoid
1978147072Sbrookspriv_script_init(char *reason, char *medium)
1979147072Sbrooks{
1980147072Sbrooks	struct interface_info *ip = ifi;
1981147072Sbrooks
1982147072Sbrooks	if (ip) {
1983147072Sbrooks		ip->client->scriptEnvsize = 100;
1984147072Sbrooks		if (ip->client->scriptEnv == NULL)
1985147072Sbrooks			ip->client->scriptEnv =
1986147072Sbrooks			    malloc(ip->client->scriptEnvsize * sizeof(char *));
1987147072Sbrooks		if (ip->client->scriptEnv == NULL)
1988147072Sbrooks			error("script_init: no memory for environment");
1989147072Sbrooks
1990147072Sbrooks		ip->client->scriptEnv[0] = strdup(CLIENT_PATH);
1991147072Sbrooks		if (ip->client->scriptEnv[0] == NULL)
1992147072Sbrooks			error("script_init: no memory for environment");
1993147072Sbrooks
1994147072Sbrooks		ip->client->scriptEnv[1] = NULL;
1995147072Sbrooks
1996147072Sbrooks		script_set_env(ip->client, "", "interface", ip->name);
1997147072Sbrooks
1998147072Sbrooks		if (medium)
1999147072Sbrooks			script_set_env(ip->client, "", "medium", medium);
2000147072Sbrooks
2001147072Sbrooks		script_set_env(ip->client, "", "reason", reason);
2002147072Sbrooks	}
2003147072Sbrooks}
2004147072Sbrooks
2005147072Sbrooksvoid
2006147072Sbrookspriv_script_write_params(char *prefix, struct client_lease *lease)
2007147072Sbrooks{
2008147072Sbrooks	struct interface_info *ip = ifi;
2009149727Sbrooks	u_int8_t dbuf[1500], *dp = NULL;
2010149727Sbrooks	int i, len;
2011147072Sbrooks	char tbuf[128];
2012147072Sbrooks
2013147072Sbrooks	script_set_env(ip->client, prefix, "ip_address",
2014147072Sbrooks	    piaddr(lease->address));
2015147072Sbrooks
2016149727Sbrooks	if (ip->client->config->default_actions[DHO_SUBNET_MASK] ==
2017149727Sbrooks	    ACTION_SUPERSEDE) {
2018149727Sbrooks		dp = ip->client->config->defaults[DHO_SUBNET_MASK].data;
2019149727Sbrooks		len = ip->client->config->defaults[DHO_SUBNET_MASK].len;
2020149727Sbrooks	} else {
2021149727Sbrooks		dp = lease->options[DHO_SUBNET_MASK].data;
2022149727Sbrooks		len = lease->options[DHO_SUBNET_MASK].len;
2023149727Sbrooks	}
2024149727Sbrooks	if (len && (len < sizeof(lease->address.iabuf))) {
2025147072Sbrooks		struct iaddr netmask, subnet, broadcast;
2026147072Sbrooks
2027149727Sbrooks		memcpy(netmask.iabuf, dp, len);
2028149727Sbrooks		netmask.len = len;
2029147072Sbrooks		subnet = subnet_number(lease->address, netmask);
2030147072Sbrooks		if (subnet.len) {
2031147072Sbrooks			script_set_env(ip->client, prefix, "network_number",
2032147072Sbrooks			    piaddr(subnet));
2033147072Sbrooks			if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
2034147072Sbrooks				broadcast = broadcast_addr(subnet, netmask);
2035147072Sbrooks				if (broadcast.len)
2036147072Sbrooks					script_set_env(ip->client, prefix,
2037147072Sbrooks					    "broadcast_address",
2038147072Sbrooks					    piaddr(broadcast));
2039147072Sbrooks			}
2040147072Sbrooks		}
2041147072Sbrooks	}
2042147072Sbrooks
2043147072Sbrooks	if (lease->filename)
2044147072Sbrooks		script_set_env(ip->client, prefix, "filename", lease->filename);
2045147072Sbrooks	if (lease->server_name)
2046147072Sbrooks		script_set_env(ip->client, prefix, "server_name",
2047147072Sbrooks		    lease->server_name);
2048147072Sbrooks	for (i = 0; i < 256; i++) {
2049149727Sbrooks		len = 0;
2050147072Sbrooks
2051147072Sbrooks		if (ip->client->config->defaults[i].len) {
2052147072Sbrooks			if (lease->options[i].len) {
2053147072Sbrooks				switch (
2054147072Sbrooks				    ip->client->config->default_actions[i]) {
2055147072Sbrooks				case ACTION_DEFAULT:
2056147072Sbrooks					dp = lease->options[i].data;
2057147072Sbrooks					len = lease->options[i].len;
2058147072Sbrooks					break;
2059147072Sbrooks				case ACTION_SUPERSEDE:
2060147072Sbrookssupersede:
2061147072Sbrooks					dp = ip->client->
2062147072Sbrooks						config->defaults[i].data;
2063147072Sbrooks					len = ip->client->
2064147072Sbrooks						config->defaults[i].len;
2065147072Sbrooks					break;
2066147072Sbrooks				case ACTION_PREPEND:
2067147072Sbrooks					len = ip->client->
2068147072Sbrooks					    config->defaults[i].len +
2069147072Sbrooks					    lease->options[i].len;
2070193765Sbrian					if (len >= sizeof(dbuf)) {
2071147072Sbrooks						warning("no space to %s %s",
2072147072Sbrooks						    "prepend option",
2073147072Sbrooks						    dhcp_options[i].name);
2074147072Sbrooks						goto supersede;
2075147072Sbrooks					}
2076147072Sbrooks					dp = dbuf;
2077147072Sbrooks					memcpy(dp,
2078147072Sbrooks						ip->client->
2079147072Sbrooks						config->defaults[i].data,
2080147072Sbrooks						ip->client->
2081147072Sbrooks						config->defaults[i].len);
2082147072Sbrooks					memcpy(dp + ip->client->
2083147072Sbrooks						config->defaults[i].len,
2084147072Sbrooks						lease->options[i].data,
2085147072Sbrooks						lease->options[i].len);
2086147072Sbrooks					dp[len] = '\0';
2087147072Sbrooks					break;
2088147072Sbrooks				case ACTION_APPEND:
2089193765Sbrian					/*
2090193765Sbrian					 * When we append, we assume that we're
2091193765Sbrian					 * appending to text.  Some MS servers
2092193765Sbrian					 * include a NUL byte at the end of
2093193765Sbrian					 * the search string provided.
2094193765Sbrian					 */
2095147072Sbrooks					len = ip->client->
2096147072Sbrooks					    config->defaults[i].len +
2097147072Sbrooks					    lease->options[i].len;
2098193765Sbrian					if (len >= sizeof(dbuf)) {
2099147072Sbrooks						warning("no space to %s %s",
2100147072Sbrooks						    "append option",
2101147072Sbrooks						    dhcp_options[i].name);
2102147072Sbrooks						goto supersede;
2103147072Sbrooks					}
2104193765Sbrian					memcpy(dbuf,
2105147072Sbrooks						lease->options[i].data,
2106147072Sbrooks						lease->options[i].len);
2107193765Sbrian					for (dp = dbuf + lease->options[i].len;
2108193765Sbrian					    dp > dbuf; dp--, len--)
2109193765Sbrian						if (dp[-1] != '\0')
2110193765Sbrian							break;
2111193765Sbrian					memcpy(dp,
2112147072Sbrooks						ip->client->
2113147072Sbrooks						config->defaults[i].data,
2114147072Sbrooks						ip->client->
2115147072Sbrooks						config->defaults[i].len);
2116193765Sbrian					dp = dbuf;
2117147072Sbrooks					dp[len] = '\0';
2118147072Sbrooks				}
2119147072Sbrooks			} else {
2120147072Sbrooks				dp = ip->client->
2121147072Sbrooks					config->defaults[i].data;
2122147072Sbrooks				len = ip->client->
2123147072Sbrooks					config->defaults[i].len;
2124147072Sbrooks			}
2125147072Sbrooks		} else if (lease->options[i].len) {
2126147072Sbrooks			len = lease->options[i].len;
2127147072Sbrooks			dp = lease->options[i].data;
2128147072Sbrooks		} else {
2129147072Sbrooks			len = 0;
2130147072Sbrooks		}
2131147072Sbrooks		if (len) {
2132147072Sbrooks			char name[256];
2133147072Sbrooks
2134147072Sbrooks			if (dhcp_option_ev_name(name, sizeof(name),
2135147072Sbrooks			    &dhcp_options[i]))
2136147072Sbrooks				script_set_env(ip->client, prefix, name,
2137147072Sbrooks				    pretty_print_option(i, dp, len, 0, 0));
2138147072Sbrooks		}
2139147072Sbrooks	}
2140147072Sbrooks	snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
2141147072Sbrooks	script_set_env(ip->client, prefix, "expiry", tbuf);
2142147072Sbrooks}
2143147072Sbrooks
2144147072Sbrooksvoid
2145147072Sbrooksscript_write_params(char *prefix, struct client_lease *lease)
2146147072Sbrooks{
2147147072Sbrooks	size_t		 fn_len = 0, sn_len = 0, pr_len = 0;
2148147072Sbrooks	struct imsg_hdr	 hdr;
2149147072Sbrooks	struct buf	*buf;
2150147072Sbrooks	int		 errs, i;
2151147072Sbrooks
2152147072Sbrooks	if (lease->filename != NULL)
2153147072Sbrooks		fn_len = strlen(lease->filename);
2154147072Sbrooks	if (lease->server_name != NULL)
2155147072Sbrooks		sn_len = strlen(lease->server_name);
2156147072Sbrooks	if (prefix != NULL)
2157147072Sbrooks		pr_len = strlen(prefix);
2158147072Sbrooks
2159147072Sbrooks	hdr.code = IMSG_SCRIPT_WRITE_PARAMS;
2160147072Sbrooks	hdr.len = sizeof(hdr) + sizeof(struct client_lease) +
2161147072Sbrooks	    sizeof(size_t) + fn_len + sizeof(size_t) + sn_len +
2162147072Sbrooks	    sizeof(size_t) + pr_len;
2163147072Sbrooks
2164147072Sbrooks	for (i = 0; i < 256; i++)
2165147072Sbrooks		hdr.len += sizeof(int) + lease->options[i].len;
2166147072Sbrooks
2167147072Sbrooks	scripttime = time(NULL);
2168147072Sbrooks
2169147072Sbrooks	if ((buf = buf_open(hdr.len)) == NULL)
2170147072Sbrooks		error("buf_open: %m");
2171147072Sbrooks
2172147072Sbrooks	errs = 0;
2173147072Sbrooks	errs += buf_add(buf, &hdr, sizeof(hdr));
2174147072Sbrooks	errs += buf_add(buf, lease, sizeof(struct client_lease));
2175147072Sbrooks	errs += buf_add(buf, &fn_len, sizeof(fn_len));
2176147072Sbrooks	errs += buf_add(buf, lease->filename, fn_len);
2177147072Sbrooks	errs += buf_add(buf, &sn_len, sizeof(sn_len));
2178147072Sbrooks	errs += buf_add(buf, lease->server_name, sn_len);
2179147072Sbrooks	errs += buf_add(buf, &pr_len, sizeof(pr_len));
2180147072Sbrooks	errs += buf_add(buf, prefix, pr_len);
2181147072Sbrooks
2182147072Sbrooks	for (i = 0; i < 256; i++) {
2183147072Sbrooks		errs += buf_add(buf, &lease->options[i].len,
2184147072Sbrooks		    sizeof(lease->options[i].len));
2185147072Sbrooks		errs += buf_add(buf, lease->options[i].data,
2186147072Sbrooks		    lease->options[i].len);
2187147072Sbrooks	}
2188147072Sbrooks
2189147072Sbrooks	if (errs)
2190147072Sbrooks		error("buf_add: %m");
2191147072Sbrooks
2192147072Sbrooks	if (buf_close(privfd, buf) == -1)
2193147072Sbrooks		error("buf_close: %m");
2194147072Sbrooks}
2195147072Sbrooks
2196147072Sbrooksint
2197147072Sbrooksscript_go(void)
2198147072Sbrooks{
2199147072Sbrooks	struct imsg_hdr	 hdr;
2200147072Sbrooks	struct buf	*buf;
2201147072Sbrooks	int		 ret;
2202147072Sbrooks
2203147072Sbrooks	hdr.code = IMSG_SCRIPT_GO;
2204147072Sbrooks	hdr.len = sizeof(struct imsg_hdr);
2205147072Sbrooks
2206147072Sbrooks	if ((buf = buf_open(hdr.len)) == NULL)
2207147072Sbrooks		error("buf_open: %m");
2208147072Sbrooks
2209147072Sbrooks	if (buf_add(buf, &hdr, sizeof(hdr)))
2210147072Sbrooks		error("buf_add: %m");
2211147072Sbrooks
2212147072Sbrooks	if (buf_close(privfd, buf) == -1)
2213147072Sbrooks		error("buf_close: %m");
2214147072Sbrooks
2215147072Sbrooks	bzero(&hdr, sizeof(hdr));
2216147072Sbrooks	buf_read(privfd, &hdr, sizeof(hdr));
2217147072Sbrooks	if (hdr.code != IMSG_SCRIPT_GO_RET)
2218147072Sbrooks		error("unexpected msg type %u", hdr.code);
2219147072Sbrooks	if (hdr.len != sizeof(hdr) + sizeof(int))
2220147072Sbrooks		error("received corrupted message");
2221147072Sbrooks	buf_read(privfd, &ret, sizeof(ret));
2222147072Sbrooks
2223209756Sbrian	scripttime = time(NULL);
2224209756Sbrian
2225147072Sbrooks	return (ret);
2226147072Sbrooks}
2227147072Sbrooks
2228147072Sbrooksint
2229147072Sbrookspriv_script_go(void)
2230147072Sbrooks{
2231147072Sbrooks	char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI";
2232147072Sbrooks	static char client_path[] = CLIENT_PATH;
2233147072Sbrooks	struct interface_info *ip = ifi;
2234147072Sbrooks	int pid, wpid, wstatus;
2235147072Sbrooks
2236147072Sbrooks	scripttime = time(NULL);
2237147072Sbrooks
2238147072Sbrooks	if (ip) {
2239147072Sbrooks		scriptName = ip->client->config->script_name;
2240147072Sbrooks		envp = ip->client->scriptEnv;
2241147072Sbrooks	} else {
2242147072Sbrooks		scriptName = top_level_config.script_name;
2243147072Sbrooks		epp[0] = reason;
2244147072Sbrooks		epp[1] = client_path;
2245147072Sbrooks		epp[2] = NULL;
2246147072Sbrooks		envp = epp;
2247147072Sbrooks	}
2248147072Sbrooks
2249147072Sbrooks	argv[0] = scriptName;
2250147072Sbrooks	argv[1] = NULL;
2251147072Sbrooks
2252147072Sbrooks	pid = fork();
2253147072Sbrooks	if (pid < 0) {
2254147072Sbrooks		error("fork: %m");
2255147072Sbrooks		wstatus = 0;
2256147072Sbrooks	} else if (pid) {
2257147072Sbrooks		do {
2258147072Sbrooks			wpid = wait(&wstatus);
2259147072Sbrooks		} while (wpid != pid && wpid > 0);
2260147072Sbrooks		if (wpid < 0) {
2261147072Sbrooks			error("wait: %m");
2262147072Sbrooks			wstatus = 0;
2263147072Sbrooks		}
2264147072Sbrooks	} else {
2265147072Sbrooks		execve(scriptName, argv, envp);
2266147072Sbrooks		error("execve (%s, ...): %m", scriptName);
2267147072Sbrooks	}
2268147072Sbrooks
2269147072Sbrooks	if (ip)
2270147072Sbrooks		script_flush_env(ip->client);
2271147072Sbrooks
2272147072Sbrooks	return (wstatus & 0xff);
2273147072Sbrooks}
2274147072Sbrooks
2275147072Sbrooksvoid
2276147072Sbrooksscript_set_env(struct client_state *client, const char *prefix,
2277147072Sbrooks    const char *name, const char *value)
2278147072Sbrooks{
2279147072Sbrooks	int i, j, namelen;
2280147072Sbrooks
2281299156Ssephe	/* No `` or $() command substitution allowed in environment values! */
2282299156Ssephe	for (j=0; j < strlen(value); j++)
2283299156Ssephe		switch (value[j]) {
2284299156Ssephe		case '`':
2285299156Ssephe		case '$':
2286299156Ssephe			warning("illegal character (%c) in value '%s'",
2287299156Ssephe			    value[j], value);
2288299156Ssephe			/* Ignore this option */
2289299156Ssephe			return;
2290299156Ssephe		}
2291299156Ssephe
2292147072Sbrooks	namelen = strlen(name);
2293147072Sbrooks
2294147072Sbrooks	for (i = 0; client->scriptEnv[i]; i++)
2295147072Sbrooks		if (strncmp(client->scriptEnv[i], name, namelen) == 0 &&
2296147072Sbrooks		    client->scriptEnv[i][namelen] == '=')
2297147072Sbrooks			break;
2298147072Sbrooks
2299147072Sbrooks	if (client->scriptEnv[i])
2300147072Sbrooks		/* Reuse the slot. */
2301147072Sbrooks		free(client->scriptEnv[i]);
2302147072Sbrooks	else {
2303147072Sbrooks		/* New variable.  Expand if necessary. */
2304147072Sbrooks		if (i >= client->scriptEnvsize - 1) {
2305147072Sbrooks			char **newscriptEnv;
2306147072Sbrooks			int newscriptEnvsize = client->scriptEnvsize + 50;
2307147072Sbrooks
2308147072Sbrooks			newscriptEnv = realloc(client->scriptEnv,
2309147072Sbrooks			    newscriptEnvsize);
2310147072Sbrooks			if (newscriptEnv == NULL) {
2311147072Sbrooks				free(client->scriptEnv);
2312147072Sbrooks				client->scriptEnv = NULL;
2313147072Sbrooks				client->scriptEnvsize = 0;
2314147072Sbrooks				error("script_set_env: no memory for variable");
2315147072Sbrooks			}
2316147072Sbrooks			client->scriptEnv = newscriptEnv;
2317147072Sbrooks			client->scriptEnvsize = newscriptEnvsize;
2318147072Sbrooks		}
2319147072Sbrooks		/* need to set the NULL pointer at end of array beyond
2320147072Sbrooks		   the new slot. */
2321147072Sbrooks		client->scriptEnv[i + 1] = NULL;
2322147072Sbrooks	}
2323147072Sbrooks	/* Allocate space and format the variable in the appropriate slot. */
2324147072Sbrooks	client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 +
2325147072Sbrooks	    strlen(value) + 1);
2326147072Sbrooks	if (client->scriptEnv[i] == NULL)
2327147072Sbrooks		error("script_set_env: no memory for variable assignment");
2328147072Sbrooks	snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) +
2329147072Sbrooks	    1 + strlen(value) + 1, "%s%s=%s", prefix, name, value);
2330147072Sbrooks}
2331147072Sbrooks
2332147072Sbrooksvoid
2333147072Sbrooksscript_flush_env(struct client_state *client)
2334147072Sbrooks{
2335147072Sbrooks	int i;
2336147072Sbrooks
2337147072Sbrooks	for (i = 0; client->scriptEnv[i]; i++) {
2338147072Sbrooks		free(client->scriptEnv[i]);
2339147072Sbrooks		client->scriptEnv[i] = NULL;
2340147072Sbrooks	}
2341147072Sbrooks	client->scriptEnvsize = 0;
2342147072Sbrooks}
2343147072Sbrooks
2344147072Sbrooksint
2345147072Sbrooksdhcp_option_ev_name(char *buf, size_t buflen, struct option *option)
2346147072Sbrooks{
2347147072Sbrooks	int i;
2348147072Sbrooks
2349147072Sbrooks	for (i = 0; option->name[i]; i++) {
2350147072Sbrooks		if (i + 1 == buflen)
2351147072Sbrooks			return 0;
2352147072Sbrooks		if (option->name[i] == '-')
2353147072Sbrooks			buf[i] = '_';
2354147072Sbrooks		else
2355147072Sbrooks			buf[i] = option->name[i];
2356147072Sbrooks	}
2357147072Sbrooks
2358147072Sbrooks	buf[i] = 0;
2359147072Sbrooks	return 1;
2360147072Sbrooks}
2361147072Sbrooks
2362147072Sbrooksvoid
2363147072Sbrooksgo_daemon(void)
2364147072Sbrooks{
2365147072Sbrooks	static int state = 0;
2366255219Spjd	cap_rights_t rights;
2367147072Sbrooks
2368147072Sbrooks	if (no_daemon || state)
2369147072Sbrooks		return;
2370147072Sbrooks
2371147072Sbrooks	state = 1;
2372147072Sbrooks
2373147072Sbrooks	/* Stop logging to stderr... */
2374147072Sbrooks	log_perror = 0;
2375147072Sbrooks
2376147072Sbrooks	if (daemon(1, 0) == -1)
2377147072Sbrooks		error("daemon");
2378147072Sbrooks
2379255219Spjd	cap_rights_init(&rights);
2380255219Spjd
2381252632Spjd	if (pidfile != NULL) {
2382226345Sdes		pidfile_write(pidfile);
2383255219Spjd		if (cap_rights_limit(pidfile_fileno(pidfile), &rights) < 0 &&
2384252632Spjd		    errno != ENOSYS) {
2385252632Spjd			error("can't limit pidfile descriptor: %m");
2386252632Spjd		}
2387252632Spjd	}
2388226345Sdes
2389147072Sbrooks	/* we are chrooted, daemon(3) fails to open /dev/null */
2390147072Sbrooks	if (nullfd != -1) {
2391147072Sbrooks		dup2(nullfd, STDIN_FILENO);
2392147072Sbrooks		dup2(nullfd, STDOUT_FILENO);
2393147072Sbrooks		dup2(nullfd, STDERR_FILENO);
2394147072Sbrooks		close(nullfd);
2395147072Sbrooks		nullfd = -1;
2396147072Sbrooks	}
2397252633Spjd
2398255219Spjd	if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS)
2399252633Spjd		error("can't limit stdin: %m");
2400255219Spjd	cap_rights_init(&rights, CAP_WRITE);
2401255219Spjd	if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS)
2402252633Spjd		error("can't limit stdout: %m");
2403255219Spjd	if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS)
2404252633Spjd		error("can't limit stderr: %m");
2405147072Sbrooks}
2406147072Sbrooks
2407147072Sbrooksint
2408147072Sbrookscheck_option(struct client_lease *l, int option)
2409147072Sbrooks{
2410147072Sbrooks	char *opbuf;
2411147072Sbrooks	char *sbuf;
2412147072Sbrooks
2413147072Sbrooks	/* we use this, since this is what gets passed to dhclient-script */
2414147072Sbrooks
2415147072Sbrooks	opbuf = pretty_print_option(option, l->options[option].data,
2416147072Sbrooks	    l->options[option].len, 0, 0);
2417147072Sbrooks
2418147072Sbrooks	sbuf = option_as_string(option, l->options[option].data,
2419147072Sbrooks	    l->options[option].len);
2420147072Sbrooks
2421147072Sbrooks	switch (option) {
2422147072Sbrooks	case DHO_SUBNET_MASK:
2423147072Sbrooks	case DHO_TIME_SERVERS:
2424147072Sbrooks	case DHO_NAME_SERVERS:
2425147072Sbrooks	case DHO_ROUTERS:
2426147072Sbrooks	case DHO_DOMAIN_NAME_SERVERS:
2427147072Sbrooks	case DHO_LOG_SERVERS:
2428147072Sbrooks	case DHO_COOKIE_SERVERS:
2429147072Sbrooks	case DHO_LPR_SERVERS:
2430147072Sbrooks	case DHO_IMPRESS_SERVERS:
2431147072Sbrooks	case DHO_RESOURCE_LOCATION_SERVERS:
2432147072Sbrooks	case DHO_SWAP_SERVER:
2433147072Sbrooks	case DHO_BROADCAST_ADDRESS:
2434147072Sbrooks	case DHO_NIS_SERVERS:
2435147072Sbrooks	case DHO_NTP_SERVERS:
2436147072Sbrooks	case DHO_NETBIOS_NAME_SERVERS:
2437147072Sbrooks	case DHO_NETBIOS_DD_SERVER:
2438147072Sbrooks	case DHO_FONT_SERVERS:
2439147072Sbrooks	case DHO_DHCP_SERVER_IDENTIFIER:
2440183974Sbrooks	case DHO_NISPLUS_SERVERS:
2441183974Sbrooks	case DHO_MOBILE_IP_HOME_AGENT:
2442147689Sbrooks	case DHO_SMTP_SERVER:
2443147689Sbrooks	case DHO_POP_SERVER:
2444147689Sbrooks	case DHO_NNTP_SERVER:
2445147689Sbrooks	case DHO_WWW_SERVER:
2446147689Sbrooks	case DHO_FINGER_SERVER:
2447147689Sbrooks	case DHO_IRC_SERVER:
2448183974Sbrooks	case DHO_STREETTALK_SERVER:
2449183974Sbrooks	case DHO_STREETTALK_DA_SERVER:
2450147072Sbrooks		if (!ipv4addrs(opbuf)) {
2451147072Sbrooks			warning("Invalid IP address in option: %s", opbuf);
2452147072Sbrooks			return (0);
2453147072Sbrooks		}
2454147072Sbrooks		return (1)  ;
2455147072Sbrooks	case DHO_HOST_NAME:
2456147072Sbrooks	case DHO_NIS_DOMAIN:
2457183974Sbrooks	case DHO_NISPLUS_DOMAIN:
2458183974Sbrooks	case DHO_TFTP_SERVER_NAME:
2459147072Sbrooks		if (!res_hnok(sbuf)) {
2460147072Sbrooks			warning("Bogus Host Name option %d: %s (%s)", option,
2461147072Sbrooks			    sbuf, opbuf);
2462153287Sbrooks			l->options[option].len = 0;
2463153287Sbrooks			free(l->options[option].data);
2464147072Sbrooks		}
2465147072Sbrooks		return (1);
2466147686Sbrooks	case DHO_DOMAIN_NAME:
2467228259Sdumbbell	case DHO_DOMAIN_SEARCH:
2468149639Sbrooks		if (!res_hnok(sbuf)) {
2469149639Sbrooks			if (!check_search(sbuf)) {
2470149639Sbrooks				warning("Bogus domain search list %d: %s (%s)",
2471149639Sbrooks				    option, sbuf, opbuf);
2472153287Sbrooks				l->options[option].len = 0;
2473153287Sbrooks				free(l->options[option].data);
2474149639Sbrooks			}
2475149639Sbrooks		}
2476149639Sbrooks		return (1);
2477147072Sbrooks	case DHO_PAD:
2478147072Sbrooks	case DHO_TIME_OFFSET:
2479147072Sbrooks	case DHO_BOOT_SIZE:
2480147072Sbrooks	case DHO_MERIT_DUMP:
2481147072Sbrooks	case DHO_ROOT_PATH:
2482147072Sbrooks	case DHO_EXTENSIONS_PATH:
2483147072Sbrooks	case DHO_IP_FORWARDING:
2484147072Sbrooks	case DHO_NON_LOCAL_SOURCE_ROUTING:
2485147072Sbrooks	case DHO_POLICY_FILTER:
2486147072Sbrooks	case DHO_MAX_DGRAM_REASSEMBLY:
2487147072Sbrooks	case DHO_DEFAULT_IP_TTL:
2488147072Sbrooks	case DHO_PATH_MTU_AGING_TIMEOUT:
2489147072Sbrooks	case DHO_PATH_MTU_PLATEAU_TABLE:
2490147072Sbrooks	case DHO_INTERFACE_MTU:
2491147072Sbrooks	case DHO_ALL_SUBNETS_LOCAL:
2492147072Sbrooks	case DHO_PERFORM_MASK_DISCOVERY:
2493147072Sbrooks	case DHO_MASK_SUPPLIER:
2494147072Sbrooks	case DHO_ROUTER_DISCOVERY:
2495147072Sbrooks	case DHO_ROUTER_SOLICITATION_ADDRESS:
2496147072Sbrooks	case DHO_STATIC_ROUTES:
2497147072Sbrooks	case DHO_TRAILER_ENCAPSULATION:
2498147072Sbrooks	case DHO_ARP_CACHE_TIMEOUT:
2499147072Sbrooks	case DHO_IEEE802_3_ENCAPSULATION:
2500147072Sbrooks	case DHO_DEFAULT_TCP_TTL:
2501147072Sbrooks	case DHO_TCP_KEEPALIVE_INTERVAL:
2502147072Sbrooks	case DHO_TCP_KEEPALIVE_GARBAGE:
2503147072Sbrooks	case DHO_VENDOR_ENCAPSULATED_OPTIONS:
2504147072Sbrooks	case DHO_NETBIOS_NODE_TYPE:
2505147072Sbrooks	case DHO_NETBIOS_SCOPE:
2506147072Sbrooks	case DHO_X_DISPLAY_MANAGER:
2507147072Sbrooks	case DHO_DHCP_REQUESTED_ADDRESS:
2508147072Sbrooks	case DHO_DHCP_LEASE_TIME:
2509147072Sbrooks	case DHO_DHCP_OPTION_OVERLOAD:
2510147072Sbrooks	case DHO_DHCP_MESSAGE_TYPE:
2511147072Sbrooks	case DHO_DHCP_PARAMETER_REQUEST_LIST:
2512147072Sbrooks	case DHO_DHCP_MESSAGE:
2513147072Sbrooks	case DHO_DHCP_MAX_MESSAGE_SIZE:
2514147072Sbrooks	case DHO_DHCP_RENEWAL_TIME:
2515147072Sbrooks	case DHO_DHCP_REBINDING_TIME:
2516147072Sbrooks	case DHO_DHCP_CLASS_IDENTIFIER:
2517147072Sbrooks	case DHO_DHCP_CLIENT_IDENTIFIER:
2518183974Sbrooks	case DHO_BOOTFILE_NAME:
2519147072Sbrooks	case DHO_DHCP_USER_CLASS_ID:
2520147072Sbrooks	case DHO_END:
2521147072Sbrooks		return (1);
2522166602Semaste	case DHO_CLASSLESS_ROUTES:
2523166602Semaste		return (check_classless_option(l->options[option].data,
2524166602Semaste		    l->options[option].len));
2525147072Sbrooks	default:
2526147072Sbrooks		warning("unknown dhcp option value 0x%x", option);
2527147072Sbrooks		return (unknown_ok);
2528147072Sbrooks	}
2529147072Sbrooks}
2530147072Sbrooks
2531166602Semaste/* RFC 3442 The Classless Static Routes option checks */
2532147072Sbrooksint
2533166602Semastecheck_classless_option(unsigned char *data, int len)
2534166602Semaste{
2535166602Semaste	int i = 0;
2536166602Semaste	unsigned char width;
2537166602Semaste	in_addr_t addr, mask;
2538166602Semaste
2539166602Semaste	if (len < 5) {
2540166602Semaste		warning("Too small length: %d", len);
2541166602Semaste		return (0);
2542166602Semaste	}
2543166602Semaste	while(i < len) {
2544166602Semaste		width = data[i++];
2545166602Semaste		if (width == 0) {
2546166602Semaste			i += 4;
2547166602Semaste			continue;
2548166602Semaste		} else if (width < 9) {
2549252621Spjd			addr =  (in_addr_t)(data[i]	<< 24);
2550166602Semaste			i += 1;
2551166602Semaste		} else if (width < 17) {
2552252621Spjd			addr =  (in_addr_t)(data[i]	<< 24) +
2553166602Semaste				(in_addr_t)(data[i + 1]	<< 16);
2554166602Semaste			i += 2;
2555166602Semaste		} else if (width < 25) {
2556252621Spjd			addr =  (in_addr_t)(data[i]	<< 24) +
2557166602Semaste				(in_addr_t)(data[i + 1]	<< 16) +
2558166602Semaste				(in_addr_t)(data[i + 2]	<< 8);
2559166602Semaste			i += 3;
2560166602Semaste		} else if (width < 33) {
2561252621Spjd			addr =  (in_addr_t)(data[i]	<< 24) +
2562166602Semaste				(in_addr_t)(data[i + 1]	<< 16) +
2563166602Semaste				(in_addr_t)(data[i + 2]	<< 8)  +
2564166602Semaste				data[i + 3];
2565166602Semaste			i += 4;
2566166602Semaste		} else {
2567166602Semaste			warning("Incorrect subnet width: %d", width);
2568166602Semaste			return (0);
2569166602Semaste		}
2570166602Semaste		mask = (in_addr_t)(~0) << (32 - width);
2571166602Semaste		addr = ntohl(addr);
2572166602Semaste		mask = ntohl(mask);
2573166602Semaste
2574166602Semaste		/*
2575166602Semaste		 * From RFC 3442:
2576166602Semaste		 * ... After deriving a subnet number and subnet mask
2577166602Semaste		 * from each destination descriptor, the DHCP client
2578166602Semaste		 * MUST zero any bits in the subnet number where the
2579166602Semaste		 * corresponding bit in the mask is zero...
2580166602Semaste		 */
2581166602Semaste		if ((addr & mask) != addr) {
2582166602Semaste			addr &= mask;
2583166602Semaste			data[i - 1] = (unsigned char)(
2584166602Semaste				(addr >> (((32 - width)/8)*8)) & 0xFF);
2585252621Spjd		}
2586166602Semaste		i += 4;
2587166602Semaste	}
2588166602Semaste	if (i > len) {
2589166602Semaste		warning("Incorrect data length: %d (must be %d)", len, i);
2590166602Semaste		return (0);
2591166602Semaste	}
2592166602Semaste	return (1);
2593166602Semaste}
2594166602Semaste
2595166602Semasteint
2596147072Sbrooksres_hnok(const char *dn)
2597147072Sbrooks{
2598147072Sbrooks	int pch = PERIOD, ch = *dn++;
2599147072Sbrooks
2600147072Sbrooks	while (ch != '\0') {
2601147072Sbrooks		int nch = *dn++;
2602147072Sbrooks
2603147072Sbrooks		if (periodchar(ch)) {
2604147072Sbrooks			;
2605147072Sbrooks		} else if (periodchar(pch)) {
2606147072Sbrooks			if (!borderchar(ch))
2607147072Sbrooks				return (0);
2608147072Sbrooks		} else if (periodchar(nch) || nch == '\0') {
2609147072Sbrooks			if (!borderchar(ch))
2610147072Sbrooks				return (0);
2611147072Sbrooks		} else {
2612147072Sbrooks			if (!middlechar(ch))
2613147072Sbrooks				return (0);
2614147072Sbrooks		}
2615147072Sbrooks		pch = ch, ch = nch;
2616147072Sbrooks	}
2617147072Sbrooks	return (1);
2618147072Sbrooks}
2619147072Sbrooks
2620149639Sbrooksint
2621149639Sbrookscheck_search(const char *srch)
2622149639Sbrooks{
2623149639Sbrooks        int pch = PERIOD, ch = *srch++;
2624149639Sbrooks	int domains = 1;
2625149639Sbrooks
2626149639Sbrooks	/* 256 char limit re resolv.conf(5) */
2627149639Sbrooks	if (strlen(srch) > 256)
2628149639Sbrooks		return (0);
2629149639Sbrooks
2630149639Sbrooks	while (whitechar(ch))
2631149639Sbrooks		ch = *srch++;
2632149639Sbrooks
2633149639Sbrooks        while (ch != '\0') {
2634149639Sbrooks                int nch = *srch++;
2635149639Sbrooks
2636149639Sbrooks                if (periodchar(ch) || whitechar(ch)) {
2637149639Sbrooks                        ;
2638149639Sbrooks                } else if (periodchar(pch)) {
2639149639Sbrooks                        if (!borderchar(ch))
2640149639Sbrooks                                return (0);
2641149639Sbrooks                } else if (periodchar(nch) || nch == '\0') {
2642149639Sbrooks                        if (!borderchar(ch))
2643149639Sbrooks                                return (0);
2644149639Sbrooks                } else {
2645149639Sbrooks                        if (!middlechar(ch))
2646149639Sbrooks                                return (0);
2647149639Sbrooks                }
2648149639Sbrooks		if (!whitechar(ch)) {
2649149639Sbrooks			pch = ch;
2650149639Sbrooks		} else {
2651149639Sbrooks			while (whitechar(nch)) {
2652149639Sbrooks				nch = *srch++;
2653149639Sbrooks			}
2654149639Sbrooks			if (nch != '\0')
2655149639Sbrooks				domains++;
2656149639Sbrooks			pch = PERIOD;
2657149639Sbrooks		}
2658149639Sbrooks		ch = nch;
2659149639Sbrooks        }
2660149639Sbrooks	/* 6 domain limit re resolv.conf(5) */
2661149639Sbrooks	if (domains > 6)
2662149639Sbrooks		return (0);
2663149639Sbrooks        return (1);
2664149639Sbrooks}
2665149639Sbrooks
2666147072Sbrooks/* Does buf consist only of dotted decimal ipv4 addrs?
2667147072Sbrooks * return how many if so,
2668147072Sbrooks * otherwise, return 0
2669147072Sbrooks */
2670147072Sbrooksint
2671147072Sbrooksipv4addrs(char * buf)
2672147072Sbrooks{
2673147072Sbrooks	struct in_addr jnk;
2674147072Sbrooks	int count = 0;
2675147072Sbrooks
2676147072Sbrooks	while (inet_aton(buf, &jnk) == 1){
2677147072Sbrooks		count++;
2678147072Sbrooks		while (periodchar(*buf) || digitchar(*buf))
2679147072Sbrooks			buf++;
2680147072Sbrooks		if (*buf == '\0')
2681147072Sbrooks			return (count);
2682147072Sbrooks		while (*buf ==  ' ')
2683147072Sbrooks			buf++;
2684147072Sbrooks	}
2685147072Sbrooks	return (0);
2686147072Sbrooks}
2687147072Sbrooks
2688147072Sbrooks
2689147072Sbrookschar *
2690147072Sbrooksoption_as_string(unsigned int code, unsigned char *data, int len)
2691147072Sbrooks{
2692147072Sbrooks	static char optbuf[32768]; /* XXX */
2693147072Sbrooks	char *op = optbuf;
2694147072Sbrooks	int opleft = sizeof(optbuf);
2695147072Sbrooks	unsigned char *dp = data;
2696147072Sbrooks
2697147072Sbrooks	if (code > 255)
2698147072Sbrooks		error("option_as_string: bad code %d", code);
2699147072Sbrooks
2700147072Sbrooks	for (; dp < data + len; dp++) {
2701147072Sbrooks		if (!isascii(*dp) || !isprint(*dp)) {
2702147072Sbrooks			if (dp + 1 != data + len || *dp != 0) {
2703147072Sbrooks				snprintf(op, opleft, "\\%03o", *dp);
2704147072Sbrooks				op += 4;
2705147072Sbrooks				opleft -= 4;
2706147072Sbrooks			}
2707147072Sbrooks		} else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
2708147072Sbrooks		    *dp == '`' || *dp == '\\') {
2709147072Sbrooks			*op++ = '\\';
2710147072Sbrooks			*op++ = *dp;
2711147072Sbrooks			opleft -= 2;
2712147072Sbrooks		} else {
2713147072Sbrooks			*op++ = *dp;
2714147072Sbrooks			opleft--;
2715147072Sbrooks		}
2716147072Sbrooks	}
2717147072Sbrooks	if (opleft < 1)
2718147072Sbrooks		goto toobig;
2719147072Sbrooks	*op = 0;
2720147072Sbrooks	return optbuf;
2721147072Sbrookstoobig:
2722147072Sbrooks	warning("dhcp option too large");
2723147072Sbrooks	return "<error>";
2724147072Sbrooks}
2725147072Sbrooks
2726147072Sbrooksint
2727147072Sbrooksfork_privchld(int fd, int fd2)
2728147072Sbrooks{
2729147072Sbrooks	struct pollfd pfd[1];
2730147072Sbrooks	int nfds;
2731147072Sbrooks
2732147072Sbrooks	switch (fork()) {
2733147072Sbrooks	case -1:
2734147072Sbrooks		error("cannot fork");
2735147072Sbrooks	case 0:
2736147072Sbrooks		break;
2737147072Sbrooks	default:
2738147072Sbrooks		return (0);
2739147072Sbrooks	}
2740147072Sbrooks
2741147072Sbrooks	setproctitle("%s [priv]", ifi->name);
2742147072Sbrooks
2743180130Sed	setsid();
2744147072Sbrooks	dup2(nullfd, STDIN_FILENO);
2745147072Sbrooks	dup2(nullfd, STDOUT_FILENO);
2746147072Sbrooks	dup2(nullfd, STDERR_FILENO);
2747147072Sbrooks	close(nullfd);
2748147072Sbrooks	close(fd2);
2749252626Spjd	close(ifi->rfdesc);
2750252626Spjd	ifi->rfdesc = -1;
2751147072Sbrooks
2752147072Sbrooks	for (;;) {
2753147072Sbrooks		pfd[0].fd = fd;
2754147072Sbrooks		pfd[0].events = POLLIN;
2755147072Sbrooks		if ((nfds = poll(pfd, 1, INFTIM)) == -1)
2756147072Sbrooks			if (errno != EINTR)
2757147072Sbrooks				error("poll error");
2758147072Sbrooks
2759147072Sbrooks		if (nfds == 0 || !(pfd[0].revents & POLLIN))
2760147072Sbrooks			continue;
2761147072Sbrooks
2762252626Spjd		dispatch_imsg(ifi, fd);
2763147072Sbrooks	}
2764147072Sbrooks}
2765