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$");
58149399Sbrooks
59147072Sbrooks#include "dhcpd.h"
60147072Sbrooks#include "privsep.h"
61147072Sbrooks
62147085Sbrooks#include <net80211/ieee80211_freebsd.h>
63147085Sbrooks
64147085Sbrooks#ifndef _PATH_VAREMPTY
65147085Sbrooks#define	_PATH_VAREMPTY	"/var/empty"
66147085Sbrooks#endif
67147085Sbrooks
68147072Sbrooks#define	PERIOD 0x2e
69147072Sbrooks#define	hyphenchar(c) ((c) == 0x2d)
70147072Sbrooks#define	bslashchar(c) ((c) == 0x5c)
71147072Sbrooks#define	periodchar(c) ((c) == PERIOD)
72147072Sbrooks#define	asterchar(c) ((c) == 0x2a)
73147072Sbrooks#define	alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
74147072Sbrooks	    ((c) >= 0x61 && (c) <= 0x7a))
75147072Sbrooks#define	digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
76149639Sbrooks#define	whitechar(c) ((c) == ' ' || (c) == '\t')
77147072Sbrooks
78147072Sbrooks#define	borderchar(c) (alphachar(c) || digitchar(c))
79147072Sbrooks#define	middlechar(c) (borderchar(c) || hyphenchar(c))
80147072Sbrooks#define	domainchar(c) ((c) > 0x20 && (c) < 0x7f)
81147072Sbrooks
82147072Sbrooks#define	CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
83147072Sbrooks
84147072Sbrookstime_t cur_time;
85147072Sbrookstime_t default_lease_time = 43200; /* 12 hours... */
86147072Sbrooks
87147072Sbrookschar *path_dhclient_conf = _PATH_DHCLIENT_CONF;
88147072Sbrookschar *path_dhclient_db = NULL;
89147072Sbrooks
90147072Sbrooksint log_perror = 1;
91147072Sbrooksint privfd;
92147072Sbrooksint nullfd = -1;
93147072Sbrooks
94147072Sbrooksstruct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
95147072Sbrooksstruct in_addr inaddr_any;
96147072Sbrooksstruct sockaddr_in sockaddr_broadcast;
97147072Sbrooks
98231277Sbaptchar *path_dhclient_pidfile;
99231277Sbaptstruct pidfh *pidfile;
100231277Sbapt
101147072Sbrooks/*
102147072Sbrooks * ASSERT_STATE() does nothing now; it used to be
103147072Sbrooks * assert (state_is == state_shouldbe).
104147072Sbrooks */
105147072Sbrooks#define ASSERT_STATE(state_is, state_shouldbe) {}
106147072Sbrooks
107147072Sbrooks#define TIME_MAX 2147483647
108147072Sbrooks
109147072Sbrooksint		log_priority;
110147072Sbrooksint		no_daemon;
111147072Sbrooksint		unknown_ok = 1;
112147072Sbrooksint		routefd;
113147072Sbrooks
114147072Sbrooksstruct interface_info	*ifi;
115147072Sbrooks
116147072Sbrooksint		 findproto(char *, int);
117147072Sbrooksstruct sockaddr	*get_ifa(char *, int);
118147072Sbrooksvoid		 routehandler(struct protocol *);
119147072Sbrooksvoid		 usage(void);
120147072Sbrooksint		 check_option(struct client_lease *l, int option);
121166602Semasteint		 check_classless_option(unsigned char *data, int len);
122147072Sbrooksint		 ipv4addrs(char * buf);
123147072Sbrooksint		 res_hnok(const char *dn);
124149639Sbrooksint		 check_search(const char *srch);
125147072Sbrookschar		*option_as_string(unsigned int code, unsigned char *data, int len);
126147072Sbrooksint		 fork_privchld(int, int);
127147072Sbrooks
128147072Sbrooks#define	ROUNDUP(a) \
129147072Sbrooks	    ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
130147072Sbrooks#define	ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
131147072Sbrooks
132209756Sbrianstatic time_t	scripttime;
133147072Sbrooks
134147072Sbrooksint
135147072Sbrooksfindproto(char *cp, int n)
136147072Sbrooks{
137147072Sbrooks	struct sockaddr *sa;
138147072Sbrooks	int i;
139147072Sbrooks
140147072Sbrooks	if (n == 0)
141147072Sbrooks		return -1;
142147072Sbrooks	for (i = 1; i; i <<= 1) {
143147072Sbrooks		if (i & n) {
144147072Sbrooks			sa = (struct sockaddr *)cp;
145147072Sbrooks			switch (i) {
146147072Sbrooks			case RTA_IFA:
147147072Sbrooks			case RTA_DST:
148147072Sbrooks			case RTA_GATEWAY:
149147072Sbrooks			case RTA_NETMASK:
150147072Sbrooks				if (sa->sa_family == AF_INET)
151147072Sbrooks					return AF_INET;
152147072Sbrooks				if (sa->sa_family == AF_INET6)
153147072Sbrooks					return AF_INET6;
154147072Sbrooks				break;
155147072Sbrooks			case RTA_IFP:
156147072Sbrooks				break;
157147072Sbrooks			}
158147072Sbrooks			ADVANCE(cp, sa);
159147072Sbrooks		}
160147072Sbrooks	}
161147072Sbrooks	return (-1);
162147072Sbrooks}
163147072Sbrooks
164147072Sbrooksstruct sockaddr *
165147072Sbrooksget_ifa(char *cp, int n)
166147072Sbrooks{
167147072Sbrooks	struct sockaddr *sa;
168147072Sbrooks	int i;
169147072Sbrooks
170147072Sbrooks	if (n == 0)
171147072Sbrooks		return (NULL);
172147072Sbrooks	for (i = 1; i; i <<= 1)
173147072Sbrooks		if (i & n) {
174147072Sbrooks			sa = (struct sockaddr *)cp;
175147072Sbrooks			if (i == RTA_IFA)
176147072Sbrooks				return (sa);
177147072Sbrooks			ADVANCE(cp, sa);
178147072Sbrooks		}
179147072Sbrooks
180147072Sbrooks	return (NULL);
181147072Sbrooks}
182177501Ssam
183147072Sbrooksstruct iaddr defaddr = { 4 };
184177501Ssamuint8_t curbssid[6];
185147072Sbrooks
186177501Ssamstatic void
187177501Ssamdisassoc(void *arg)
188177501Ssam{
189177501Ssam	struct interface_info *ifi = arg;
190177501Ssam
191177501Ssam	/*
192177501Ssam	 * Clear existing state.
193177501Ssam	 */
194177501Ssam	if (ifi->client->active != NULL) {
195177501Ssam		script_init("EXPIRE", NULL);
196177501Ssam		script_write_params("old_",
197177501Ssam		    ifi->client->active);
198177501Ssam		if (ifi->client->alias)
199177501Ssam			script_write_params("alias_",
200177501Ssam				ifi->client->alias);
201177501Ssam		script_go();
202177501Ssam	}
203177501Ssam	ifi->client->state = S_INIT;
204177501Ssam}
205177501Ssam
206147072Sbrooks/* ARGSUSED */
207147072Sbrooksvoid
208147072Sbrooksroutehandler(struct protocol *p)
209147072Sbrooks{
210209756Sbrian	char msg[2048], *addr;
211147072Sbrooks	struct rt_msghdr *rtm;
212147072Sbrooks	struct if_msghdr *ifm;
213147072Sbrooks	struct ifa_msghdr *ifam;
214147072Sbrooks	struct if_announcemsghdr *ifan;
215177501Ssam	struct ieee80211_join_event *jev;
216147072Sbrooks	struct client_lease *l;
217147072Sbrooks	time_t t = time(NULL);
218147072Sbrooks	struct sockaddr *sa;
219147072Sbrooks	struct iaddr a;
220147072Sbrooks	ssize_t n;
221247335Sjhb	int linkstat;
222147072Sbrooks
223147072Sbrooks	n = read(routefd, &msg, sizeof(msg));
224147072Sbrooks	rtm = (struct rt_msghdr *)msg;
225147072Sbrooks	if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen ||
226147072Sbrooks	    rtm->rtm_version != RTM_VERSION)
227147072Sbrooks		return;
228147072Sbrooks
229147072Sbrooks	switch (rtm->rtm_type) {
230147072Sbrooks	case RTM_NEWADDR:
231154161Sbrooks	case RTM_DELADDR:
232147072Sbrooks		ifam = (struct ifa_msghdr *)rtm;
233154161Sbrooks
234147072Sbrooks		if (ifam->ifam_index != ifi->index)
235147072Sbrooks			break;
236147072Sbrooks		if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET)
237147072Sbrooks			break;
238154161Sbrooks		if (scripttime == 0 || t < scripttime + 10)
239154161Sbrooks			break;
240154161Sbrooks
241147072Sbrooks		sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs);
242147072Sbrooks		if (sa == NULL)
243209756Sbrian			break;
244147072Sbrooks
245147072Sbrooks		if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf))
246147072Sbrooks			error("king bula sez: len mismatch");
247147072Sbrooks		memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len);
248147072Sbrooks		if (addr_eq(a, defaddr))
249147072Sbrooks			break;
250147072Sbrooks
251147072Sbrooks		for (l = ifi->client->active; l != NULL; l = l->next)
252147072Sbrooks			if (addr_eq(a, l->address))
253147072Sbrooks				break;
254147072Sbrooks
255209756Sbrian		if (l == NULL)	/* added/deleted addr is not the one we set */
256147072Sbrooks			break;
257209756Sbrian
258209756Sbrian		addr = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
259209756Sbrian		if (rtm->rtm_type == RTM_NEWADDR)  {
260209756Sbrian			/*
261209756Sbrian			 * XXX: If someone other than us adds our address,
262209756Sbrian			 * should we assume they are taking over from us,
263209756Sbrian			 * delete the lease record, and exit without modifying
264209756Sbrian			 * the interface?
265209756Sbrian			 */
266209756Sbrian			warning("My address (%s) was re-added", addr);
267209756Sbrian		} else {
268209756Sbrian			warning("My address (%s) was deleted, dhclient exiting",
269209756Sbrian			    addr);
270209756Sbrian			goto die;
271209756Sbrian		}
272209756Sbrian		break;
273147072Sbrooks	case RTM_IFINFO:
274147072Sbrooks		ifm = (struct if_msghdr *)rtm;
275147072Sbrooks		if (ifm->ifm_index != ifi->index)
276147072Sbrooks			break;
277209756Sbrian		if ((rtm->rtm_flags & RTF_UP) == 0) {
278209756Sbrian			warning("Interface %s is down, dhclient exiting",
279209756Sbrian			    ifi->name);
280147072Sbrooks			goto die;
281209756Sbrian		}
282247335Sjhb		linkstat = interface_link_status(ifi->name);
283247335Sjhb		if (linkstat != ifi->linkstat) {
284247335Sjhb			debug("%s link state %s -> %s", ifi->name,
285247335Sjhb			    ifi->linkstat ? "up" : "down",
286247335Sjhb			    linkstat ? "up" : "down");
287247335Sjhb			ifi->linkstat = linkstat;
288247335Sjhb			if (linkstat)
289247335Sjhb				state_reboot(ifi);
290247335Sjhb		}
291147072Sbrooks		break;
292147072Sbrooks	case RTM_IFANNOUNCE:
293147072Sbrooks		ifan = (struct if_announcemsghdr *)rtm;
294147072Sbrooks		if (ifan->ifan_what == IFAN_DEPARTURE &&
295209756Sbrian		    ifan->ifan_index == ifi->index) {
296209756Sbrian			warning("Interface %s is gone, dhclient exiting",
297209756Sbrian			    ifi->name);
298147072Sbrooks			goto die;
299209756Sbrian		}
300147072Sbrooks		break;
301147085Sbrooks	case RTM_IEEE80211:
302147085Sbrooks		ifan = (struct if_announcemsghdr *)rtm;
303147085Sbrooks		if (ifan->ifan_index != ifi->index)
304147085Sbrooks			break;
305147085Sbrooks		switch (ifan->ifan_what) {
306147085Sbrooks		case RTM_IEEE80211_ASSOC:
307148373Ssam		case RTM_IEEE80211_REASSOC:
308147085Sbrooks			/*
309177501Ssam			 * Use assoc/reassoc event to kick state machine
310177501Ssam			 * in case we roam.  Otherwise fall back to the
311177501Ssam			 * normal state machine just like a wired network.
312147085Sbrooks			 */
313177501Ssam			jev = (struct ieee80211_join_event *) &ifan[1];
314177501Ssam			if (memcmp(curbssid, jev->iev_addr, 6)) {
315177501Ssam				disassoc(ifi);
316177501Ssam				state_reboot(ifi);
317147351Sbrooks			}
318177501Ssam			memcpy(curbssid, jev->iev_addr, 6);
319147085Sbrooks			break;
320147085Sbrooks		}
321147085Sbrooks		break;
322147072Sbrooks	default:
323147072Sbrooks		break;
324147072Sbrooks	}
325147072Sbrooks	return;
326147072Sbrooks
327147072Sbrooksdie:
328147072Sbrooks	script_init("FAIL", NULL);
329147072Sbrooks	if (ifi->client->alias)
330147072Sbrooks		script_write_params("alias_", ifi->client->alias);
331147072Sbrooks	script_go();
332231277Sbapt	if (pidfile != NULL)
333231277Sbapt		pidfile_remove(pidfile);
334147072Sbrooks	exit(1);
335147072Sbrooks}
336147072Sbrooks
337147072Sbrooksint
338147072Sbrooksmain(int argc, char *argv[])
339147072Sbrooks{
340147072Sbrooks	extern char		*__progname;
341147072Sbrooks	int			 ch, fd, quiet = 0, i = 0;
342147072Sbrooks	int			 pipe_fd[2];
343147085Sbrooks	int			 immediate_daemon = 0;
344147072Sbrooks	struct passwd		*pw;
345231277Sbapt	pid_t			 otherpid;
346147072Sbrooks
347147072Sbrooks	/* Initially, log errors to stderr as well as to syslogd. */
348147072Sbrooks	openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
349177500Ssam	setlogmask(LOG_UPTO(LOG_DEBUG));
350147072Sbrooks
351231277Sbapt	while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1)
352147072Sbrooks		switch (ch) {
353147085Sbrooks		case 'b':
354147085Sbrooks			immediate_daemon = 1;
355147085Sbrooks			break;
356147072Sbrooks		case 'c':
357147072Sbrooks			path_dhclient_conf = optarg;
358147072Sbrooks			break;
359147072Sbrooks		case 'd':
360147072Sbrooks			no_daemon = 1;
361147072Sbrooks			break;
362147072Sbrooks		case 'l':
363147072Sbrooks			path_dhclient_db = optarg;
364147072Sbrooks			break;
365231277Sbapt		case 'p':
366231277Sbapt			path_dhclient_pidfile = optarg;
367231277Sbapt			break;
368147072Sbrooks		case 'q':
369147072Sbrooks			quiet = 1;
370147072Sbrooks			break;
371147072Sbrooks		case 'u':
372147072Sbrooks			unknown_ok = 0;
373147072Sbrooks			break;
374147072Sbrooks		default:
375147072Sbrooks			usage();
376147072Sbrooks		}
377147072Sbrooks
378147072Sbrooks	argc -= optind;
379147072Sbrooks	argv += optind;
380147072Sbrooks
381147072Sbrooks	if (argc != 1)
382147072Sbrooks		usage();
383147072Sbrooks
384231277Sbapt	if (path_dhclient_pidfile == NULL) {
385231277Sbapt		asprintf(&path_dhclient_pidfile,
386231277Sbapt		    "%sdhclient.%s.pid", _PATH_VARRUN, *argv);
387231277Sbapt		if (path_dhclient_pidfile == NULL)
388231277Sbapt			error("asprintf");
389231277Sbapt	}
390231277Sbapt	pidfile = pidfile_open(path_dhclient_pidfile, 0600, &otherpid);
391231277Sbapt	if (pidfile == NULL) {
392231277Sbapt		if (errno == EEXIST)
393231277Sbapt			error("dhclient already running, pid: %d.", otherpid);
394231277Sbapt		if (errno == EAGAIN)
395231277Sbapt			error("dhclient already running.");
396231277Sbapt		warning("Cannot open or create pidfile: %m");
397231277Sbapt	}
398231277Sbapt
399147072Sbrooks	if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL)
400147072Sbrooks		error("calloc");
401147072Sbrooks	if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ)
402147072Sbrooks		error("Interface name too long");
403147072Sbrooks	if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s",
404147072Sbrooks	    _PATH_DHCLIENT_DB, ifi->name) == -1)
405147072Sbrooks		error("asprintf");
406147072Sbrooks
407147072Sbrooks	if (quiet)
408147072Sbrooks		log_perror = 0;
409147072Sbrooks
410147072Sbrooks	tzset();
411147072Sbrooks	time(&cur_time);
412147072Sbrooks
413147072Sbrooks	memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
414147072Sbrooks	sockaddr_broadcast.sin_family = AF_INET;
415147072Sbrooks	sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
416147072Sbrooks	sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
417147072Sbrooks	sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast);
418147072Sbrooks	inaddr_any.s_addr = INADDR_ANY;
419147072Sbrooks
420147072Sbrooks	read_client_conf();
421147072Sbrooks
422231277Sbapt	/* The next bit is potentially very time-consuming, so write out
423231277Sbapt	   the pidfile right away.  We will write it out again with the
424231277Sbapt	   correct pid after daemonizing. */
425231277Sbapt	if (pidfile != NULL)
426231277Sbapt		pidfile_write(pidfile);
427231277Sbapt
428147072Sbrooks	if (!interface_link_status(ifi->name)) {
429147072Sbrooks		fprintf(stderr, "%s: no link ...", ifi->name);
430147072Sbrooks		fflush(stderr);
431147072Sbrooks		sleep(1);
432147072Sbrooks		while (!interface_link_status(ifi->name)) {
433147072Sbrooks			fprintf(stderr, ".");
434147072Sbrooks			fflush(stderr);
435147072Sbrooks			if (++i > 10) {
436161514Sbrian				fprintf(stderr, " giving up\n");
437161514Sbrian				exit(1);
438147072Sbrooks			}
439147072Sbrooks			sleep(1);
440147072Sbrooks		}
441161514Sbrian		fprintf(stderr, " got link\n");
442147072Sbrooks	}
443247335Sjhb	ifi->linkstat = 1;
444147072Sbrooks
445147072Sbrooks	if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
446147072Sbrooks		error("cannot open %s: %m", _PATH_DEVNULL);
447147072Sbrooks
448147072Sbrooks	if ((pw = getpwnam("_dhcp")) == NULL) {
449147072Sbrooks		warning("no such user: _dhcp, falling back to \"nobody\"");
450147072Sbrooks		if ((pw = getpwnam("nobody")) == NULL)
451147072Sbrooks			error("no such user: nobody");
452147072Sbrooks	}
453147072Sbrooks
454147072Sbrooks	if (pipe(pipe_fd) == -1)
455147072Sbrooks		error("pipe");
456147072Sbrooks
457147072Sbrooks	fork_privchld(pipe_fd[0], pipe_fd[1]);
458147072Sbrooks
459147072Sbrooks	close(pipe_fd[0]);
460147072Sbrooks	privfd = pipe_fd[1];
461147072Sbrooks
462147072Sbrooks	if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
463147072Sbrooks		error("can't open and lock %s: %m", path_dhclient_db);
464147072Sbrooks	read_client_leases();
465147072Sbrooks	rewrite_client_leases();
466147072Sbrooks	close(fd);
467147072Sbrooks
468147072Sbrooks	priv_script_init("PREINIT", NULL);
469147072Sbrooks	if (ifi->client->alias)
470147072Sbrooks		priv_script_write_params("alias_", ifi->client->alias);
471147072Sbrooks	priv_script_go();
472147072Sbrooks
473147072Sbrooks	if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
474147072Sbrooks		add_protocol("AF_ROUTE", routefd, routehandler, ifi);
475147072Sbrooks
476147072Sbrooks	/* set up the interface */
477147072Sbrooks	discover_interfaces(ifi);
478147072Sbrooks
479147072Sbrooks	if (chroot(_PATH_VAREMPTY) == -1)
480147072Sbrooks		error("chroot");
481147072Sbrooks	if (chdir("/") == -1)
482147072Sbrooks		error("chdir(\"/\")");
483147072Sbrooks
484147072Sbrooks	if (setgroups(1, &pw->pw_gid) ||
485147072Sbrooks	    setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
486147072Sbrooks	    seteuid(pw->pw_uid) || setuid(pw->pw_uid))
487147072Sbrooks		error("can't drop privileges: %m");
488147072Sbrooks
489147072Sbrooks	endpwent();
490147072Sbrooks
491147072Sbrooks	setproctitle("%s", ifi->name);
492147072Sbrooks
493147085Sbrooks	if (immediate_daemon)
494147085Sbrooks		go_daemon();
495147085Sbrooks
496147072Sbrooks	ifi->client->state = S_INIT;
497147072Sbrooks	state_reboot(ifi);
498147072Sbrooks
499147072Sbrooks	bootp_packet_handler = do_packet;
500147072Sbrooks
501147072Sbrooks	dispatch();
502147072Sbrooks
503147072Sbrooks	/* not reached */
504147072Sbrooks	return (0);
505147072Sbrooks}
506147072Sbrooks
507147072Sbrooksvoid
508147072Sbrooksusage(void)
509147072Sbrooks{
510147072Sbrooks	extern char	*__progname;
511147072Sbrooks
512161514Sbrian	fprintf(stderr, "usage: %s [-bdqu] ", __progname);
513147072Sbrooks	fprintf(stderr, "[-c conffile] [-l leasefile] interface\n");
514147072Sbrooks	exit(1);
515147072Sbrooks}
516147072Sbrooks
517147072Sbrooks/*
518147072Sbrooks * Individual States:
519147072Sbrooks *
520147072Sbrooks * Each routine is called from the dhclient_state_machine() in one of
521147072Sbrooks * these conditions:
522147072Sbrooks * -> entering INIT state
523147072Sbrooks * -> recvpacket_flag == 0: timeout in this state
524147072Sbrooks * -> otherwise: received a packet in this state
525147072Sbrooks *
526147072Sbrooks * Return conditions as handled by dhclient_state_machine():
527147072Sbrooks * Returns 1, sendpacket_flag = 1: send packet, reset timer.
528147072Sbrooks * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
529147072Sbrooks * Returns 0: finish the nap which was interrupted for no good reason.
530147072Sbrooks *
531147072Sbrooks * Several per-interface variables are used to keep track of the process:
532147072Sbrooks *   active_lease: the lease that is being used on the interface
533147072Sbrooks *                 (null pointer if not configured yet).
534147072Sbrooks *   offered_leases: leases corresponding to DHCPOFFER messages that have
535147072Sbrooks *                   been sent to us by DHCP servers.
536147072Sbrooks *   acked_leases: leases corresponding to DHCPACK messages that have been
537147072Sbrooks *                 sent to us by DHCP servers.
538147072Sbrooks *   sendpacket: DHCP packet we're trying to send.
539147072Sbrooks *   destination: IP address to send sendpacket to
540147072Sbrooks * In addition, there are several relevant per-lease variables.
541147072Sbrooks *   T1_expiry, T2_expiry, lease_expiry: lease milestones
542147072Sbrooks * In the active lease, these control the process of renewing the lease;
543147072Sbrooks * In leases on the acked_leases list, this simply determines when we
544147072Sbrooks * can no longer legitimately use the lease.
545147072Sbrooks */
546147072Sbrooks
547147072Sbrooksvoid
548147072Sbrooksstate_reboot(void *ipp)
549147072Sbrooks{
550147072Sbrooks	struct interface_info *ip = ipp;
551147072Sbrooks
552147072Sbrooks	/* If we don't remember an active lease, go straight to INIT. */
553147072Sbrooks	if (!ip->client->active || ip->client->active->is_bootp) {
554147072Sbrooks		state_init(ip);
555147072Sbrooks		return;
556147072Sbrooks	}
557147072Sbrooks
558147072Sbrooks	/* We are in the rebooting state. */
559147072Sbrooks	ip->client->state = S_REBOOTING;
560147072Sbrooks
561147072Sbrooks	/* make_request doesn't initialize xid because it normally comes
562147072Sbrooks	   from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
563147072Sbrooks	   so pick an xid now. */
564147072Sbrooks	ip->client->xid = arc4random();
565147072Sbrooks
566147072Sbrooks	/* Make a DHCPREQUEST packet, and set appropriate per-interface
567147072Sbrooks	   flags. */
568147072Sbrooks	make_request(ip, ip->client->active);
569147072Sbrooks	ip->client->destination = iaddr_broadcast;
570147072Sbrooks	ip->client->first_sending = cur_time;
571147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
572147072Sbrooks
573147072Sbrooks	/* Zap the medium list... */
574147072Sbrooks	ip->client->medium = NULL;
575147072Sbrooks
576147072Sbrooks	/* Send out the first DHCPREQUEST packet. */
577147072Sbrooks	send_request(ip);
578147072Sbrooks}
579147072Sbrooks
580147072Sbrooks/*
581147072Sbrooks * Called when a lease has completely expired and we've
582147072Sbrooks * been unable to renew it.
583147072Sbrooks */
584147072Sbrooksvoid
585147072Sbrooksstate_init(void *ipp)
586147072Sbrooks{
587147072Sbrooks	struct interface_info *ip = ipp;
588147072Sbrooks
589147072Sbrooks	ASSERT_STATE(state, S_INIT);
590147072Sbrooks
591147072Sbrooks	/* Make a DHCPDISCOVER packet, and set appropriate per-interface
592147072Sbrooks	   flags. */
593147072Sbrooks	make_discover(ip, ip->client->active);
594147072Sbrooks	ip->client->xid = ip->client->packet.xid;
595147072Sbrooks	ip->client->destination = iaddr_broadcast;
596147072Sbrooks	ip->client->state = S_SELECTING;
597147072Sbrooks	ip->client->first_sending = cur_time;
598147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
599147072Sbrooks
600147072Sbrooks	/* Add an immediate timeout to cause the first DHCPDISCOVER packet
601147072Sbrooks	   to go out. */
602147072Sbrooks	send_discover(ip);
603147072Sbrooks}
604147072Sbrooks
605147072Sbrooks/*
606147072Sbrooks * state_selecting is called when one or more DHCPOFFER packets
607147072Sbrooks * have been received and a configurable period of time has passed.
608147072Sbrooks */
609147072Sbrooksvoid
610147072Sbrooksstate_selecting(void *ipp)
611147072Sbrooks{
612147072Sbrooks	struct interface_info *ip = ipp;
613147072Sbrooks	struct client_lease *lp, *next, *picked;
614147072Sbrooks
615147072Sbrooks	ASSERT_STATE(state, S_SELECTING);
616147072Sbrooks
617147072Sbrooks	/* Cancel state_selecting and send_discover timeouts, since either
618147072Sbrooks	   one could have got us here. */
619147072Sbrooks	cancel_timeout(state_selecting, ip);
620147072Sbrooks	cancel_timeout(send_discover, ip);
621147072Sbrooks
622147072Sbrooks	/* We have received one or more DHCPOFFER packets.   Currently,
623147072Sbrooks	   the only criterion by which we judge leases is whether or
624147072Sbrooks	   not we get a response when we arp for them. */
625147072Sbrooks	picked = NULL;
626147072Sbrooks	for (lp = ip->client->offered_leases; lp; lp = next) {
627147072Sbrooks		next = lp->next;
628147072Sbrooks
629147072Sbrooks		/* Check to see if we got an ARPREPLY for the address
630147072Sbrooks		   in this particular lease. */
631147072Sbrooks		if (!picked) {
632147072Sbrooks			script_init("ARPCHECK", lp->medium);
633147072Sbrooks			script_write_params("check_", lp);
634147072Sbrooks
635147072Sbrooks			/* If the ARPCHECK code detects another
636147072Sbrooks			   machine using the offered address, it exits
637147072Sbrooks			   nonzero.  We need to send a DHCPDECLINE and
638147072Sbrooks			   toss the lease. */
639147072Sbrooks			if (script_go()) {
640147072Sbrooks				make_decline(ip, lp);
641147072Sbrooks				send_decline(ip);
642147072Sbrooks				goto freeit;
643147072Sbrooks			}
644147072Sbrooks			picked = lp;
645147072Sbrooks			picked->next = NULL;
646147072Sbrooks		} else {
647147072Sbrooksfreeit:
648147072Sbrooks			free_client_lease(lp);
649147072Sbrooks		}
650147072Sbrooks	}
651147072Sbrooks	ip->client->offered_leases = NULL;
652147072Sbrooks
653147072Sbrooks	/* If we just tossed all the leases we were offered, go back
654147072Sbrooks	   to square one. */
655147072Sbrooks	if (!picked) {
656147072Sbrooks		ip->client->state = S_INIT;
657147072Sbrooks		state_init(ip);
658147072Sbrooks		return;
659147072Sbrooks	}
660147072Sbrooks
661147072Sbrooks	/* If it was a BOOTREPLY, we can just take the address right now. */
662147072Sbrooks	if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
663147072Sbrooks		ip->client->new = picked;
664147072Sbrooks
665147072Sbrooks		/* Make up some lease expiry times
666147072Sbrooks		   XXX these should be configurable. */
667147072Sbrooks		ip->client->new->expiry = cur_time + 12000;
668147072Sbrooks		ip->client->new->renewal += cur_time + 8000;
669147072Sbrooks		ip->client->new->rebind += cur_time + 10000;
670147072Sbrooks
671147072Sbrooks		ip->client->state = S_REQUESTING;
672147072Sbrooks
673147072Sbrooks		/* Bind to the address we received. */
674147072Sbrooks		bind_lease(ip);
675147072Sbrooks		return;
676147072Sbrooks	}
677147072Sbrooks
678147072Sbrooks	/* Go to the REQUESTING state. */
679147072Sbrooks	ip->client->destination = iaddr_broadcast;
680147072Sbrooks	ip->client->state = S_REQUESTING;
681147072Sbrooks	ip->client->first_sending = cur_time;
682147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
683147072Sbrooks
684147072Sbrooks	/* Make a DHCPREQUEST packet from the lease we picked. */
685147072Sbrooks	make_request(ip, picked);
686147072Sbrooks	ip->client->xid = ip->client->packet.xid;
687147072Sbrooks
688147072Sbrooks	/* Toss the lease we picked - we'll get it back in a DHCPACK. */
689147072Sbrooks	free_client_lease(picked);
690147072Sbrooks
691147072Sbrooks	/* Add an immediate timeout to send the first DHCPREQUEST packet. */
692147072Sbrooks	send_request(ip);
693147072Sbrooks}
694147072Sbrooks
695147072Sbrooks/* state_requesting is called when we receive a DHCPACK message after
696147072Sbrooks   having sent out one or more DHCPREQUEST packets. */
697147072Sbrooks
698147072Sbrooksvoid
699147072Sbrooksdhcpack(struct packet *packet)
700147072Sbrooks{
701147072Sbrooks	struct interface_info *ip = packet->interface;
702147072Sbrooks	struct client_lease *lease;
703147072Sbrooks
704147072Sbrooks	/* If we're not receptive to an offer right now, or if the offer
705147072Sbrooks	   has an unrecognizable transaction id, then just drop it. */
706147072Sbrooks	if (packet->interface->client->xid != packet->raw->xid ||
707147072Sbrooks	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
708147072Sbrooks	    (memcmp(packet->interface->hw_address.haddr,
709147072Sbrooks	    packet->raw->chaddr, packet->raw->hlen)))
710147072Sbrooks		return;
711147072Sbrooks
712147072Sbrooks	if (ip->client->state != S_REBOOTING &&
713147072Sbrooks	    ip->client->state != S_REQUESTING &&
714147072Sbrooks	    ip->client->state != S_RENEWING &&
715147072Sbrooks	    ip->client->state != S_REBINDING)
716147072Sbrooks		return;
717147072Sbrooks
718147072Sbrooks	note("DHCPACK from %s", piaddr(packet->client_addr));
719147072Sbrooks
720147072Sbrooks	lease = packet_to_lease(packet);
721147072Sbrooks	if (!lease) {
722147072Sbrooks		note("packet_to_lease failed.");
723147072Sbrooks		return;
724147072Sbrooks	}
725147072Sbrooks
726147072Sbrooks	ip->client->new = lease;
727147072Sbrooks
728147072Sbrooks	/* Stop resending DHCPREQUEST. */
729147072Sbrooks	cancel_timeout(send_request, ip);
730147072Sbrooks
731147072Sbrooks	/* Figure out the lease time. */
732147072Sbrooks	if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
733147072Sbrooks		ip->client->new->expiry = getULong(
734147072Sbrooks		    ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
735147072Sbrooks	else
736147072Sbrooks		ip->client->new->expiry = default_lease_time;
737147072Sbrooks	/* A number that looks negative here is really just very large,
738147072Sbrooks	   because the lease expiry offset is unsigned. */
739147072Sbrooks	if (ip->client->new->expiry < 0)
740147072Sbrooks		ip->client->new->expiry = TIME_MAX;
741147072Sbrooks	/* XXX should be fixed by resetting the client state */
742147072Sbrooks	if (ip->client->new->expiry < 60)
743147072Sbrooks		ip->client->new->expiry = 60;
744147072Sbrooks
745147072Sbrooks	/* Take the server-provided renewal time if there is one;
746147072Sbrooks	   otherwise figure it out according to the spec. */
747147072Sbrooks	if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
748147072Sbrooks		ip->client->new->renewal = getULong(
749147072Sbrooks		    ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
750147072Sbrooks	else
751147072Sbrooks		ip->client->new->renewal = ip->client->new->expiry / 2;
752147072Sbrooks
753147072Sbrooks	/* Same deal with the rebind time. */
754147072Sbrooks	if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
755147072Sbrooks		ip->client->new->rebind = getULong(
756147072Sbrooks		    ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
757147072Sbrooks	else
758147072Sbrooks		ip->client->new->rebind = ip->client->new->renewal +
759147072Sbrooks		    ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
760147072Sbrooks
761147072Sbrooks	ip->client->new->expiry += cur_time;
762147072Sbrooks	/* Lease lengths can never be negative. */
763147072Sbrooks	if (ip->client->new->expiry < cur_time)
764147072Sbrooks		ip->client->new->expiry = TIME_MAX;
765147072Sbrooks	ip->client->new->renewal += cur_time;
766147072Sbrooks	if (ip->client->new->renewal < cur_time)
767147072Sbrooks		ip->client->new->renewal = TIME_MAX;
768147072Sbrooks	ip->client->new->rebind += cur_time;
769147072Sbrooks	if (ip->client->new->rebind < cur_time)
770147072Sbrooks		ip->client->new->rebind = TIME_MAX;
771147072Sbrooks
772147072Sbrooks	bind_lease(ip);
773147072Sbrooks}
774147072Sbrooks
775147072Sbrooksvoid
776147072Sbrooksbind_lease(struct interface_info *ip)
777147072Sbrooks{
778147072Sbrooks	/* Remember the medium. */
779147072Sbrooks	ip->client->new->medium = ip->client->medium;
780147072Sbrooks
781147072Sbrooks	/* Write out the new lease. */
782147072Sbrooks	write_client_lease(ip, ip->client->new, 0);
783147072Sbrooks
784147072Sbrooks	/* Run the client script with the new parameters. */
785147072Sbrooks	script_init((ip->client->state == S_REQUESTING ? "BOUND" :
786147072Sbrooks	    (ip->client->state == S_RENEWING ? "RENEW" :
787147072Sbrooks	    (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))),
788147072Sbrooks	    ip->client->new->medium);
789147072Sbrooks	if (ip->client->active && ip->client->state != S_REBOOTING)
790147072Sbrooks		script_write_params("old_", ip->client->active);
791147072Sbrooks	script_write_params("new_", ip->client->new);
792147072Sbrooks	if (ip->client->alias)
793147072Sbrooks		script_write_params("alias_", ip->client->alias);
794147072Sbrooks	script_go();
795147072Sbrooks
796147072Sbrooks	/* Replace the old active lease with the new one. */
797147072Sbrooks	if (ip->client->active)
798147072Sbrooks		free_client_lease(ip->client->active);
799147072Sbrooks	ip->client->active = ip->client->new;
800147072Sbrooks	ip->client->new = NULL;
801147072Sbrooks
802147072Sbrooks	/* Set up a timeout to start the renewal process. */
803147072Sbrooks	add_timeout(ip->client->active->renewal, state_bound, ip);
804147072Sbrooks
805147072Sbrooks	note("bound to %s -- renewal in %d seconds.",
806147072Sbrooks	    piaddr(ip->client->active->address),
807147106Sbrooks	    (int)(ip->client->active->renewal - cur_time));
808147072Sbrooks	ip->client->state = S_BOUND;
809147072Sbrooks	reinitialize_interfaces();
810147072Sbrooks	go_daemon();
811147072Sbrooks}
812147072Sbrooks
813147072Sbrooks/*
814147072Sbrooks * state_bound is called when we've successfully bound to a particular
815147072Sbrooks * lease, but the renewal time on that lease has expired.   We are
816147072Sbrooks * expected to unicast a DHCPREQUEST to the server that gave us our
817147072Sbrooks * original lease.
818147072Sbrooks */
819147072Sbrooksvoid
820147072Sbrooksstate_bound(void *ipp)
821147072Sbrooks{
822147072Sbrooks	struct interface_info *ip = ipp;
823147072Sbrooks
824147072Sbrooks	ASSERT_STATE(state, S_BOUND);
825147072Sbrooks
826147072Sbrooks	/* T1 has expired. */
827147072Sbrooks	make_request(ip, ip->client->active);
828147072Sbrooks	ip->client->xid = ip->client->packet.xid;
829147072Sbrooks
830147072Sbrooks	if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
831147072Sbrooks		memcpy(ip->client->destination.iabuf, ip->client->active->
832147072Sbrooks		    options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
833147072Sbrooks		ip->client->destination.len = 4;
834147072Sbrooks	} else
835147072Sbrooks		ip->client->destination = iaddr_broadcast;
836147072Sbrooks
837147072Sbrooks	ip->client->first_sending = cur_time;
838147072Sbrooks	ip->client->interval = ip->client->config->initial_interval;
839147072Sbrooks	ip->client->state = S_RENEWING;
840147072Sbrooks
841147072Sbrooks	/* Send the first packet immediately. */
842147072Sbrooks	send_request(ip);
843147072Sbrooks}
844147072Sbrooks
845147072Sbrooksvoid
846147072Sbrooksbootp(struct packet *packet)
847147072Sbrooks{
848147072Sbrooks	struct iaddrlist *ap;
849147072Sbrooks
850147072Sbrooks	if (packet->raw->op != BOOTREPLY)
851147072Sbrooks		return;
852147072Sbrooks
853147072Sbrooks	/* If there's a reject list, make sure this packet's sender isn't
854147072Sbrooks	   on it. */
855147072Sbrooks	for (ap = packet->interface->client->config->reject_list;
856147072Sbrooks	    ap; ap = ap->next) {
857147072Sbrooks		if (addr_eq(packet->client_addr, ap->addr)) {
858147072Sbrooks			note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
859147072Sbrooks			return;
860147072Sbrooks		}
861147072Sbrooks	}
862147072Sbrooks	dhcpoffer(packet);
863147072Sbrooks}
864147072Sbrooks
865147072Sbrooksvoid
866147072Sbrooksdhcp(struct packet *packet)
867147072Sbrooks{
868147072Sbrooks	struct iaddrlist *ap;
869147072Sbrooks	void (*handler)(struct packet *);
870147072Sbrooks	char *type;
871147072Sbrooks
872147072Sbrooks	switch (packet->packet_type) {
873147072Sbrooks	case DHCPOFFER:
874147072Sbrooks		handler = dhcpoffer;
875147072Sbrooks		type = "DHCPOFFER";
876147072Sbrooks		break;
877147072Sbrooks	case DHCPNAK:
878147072Sbrooks		handler = dhcpnak;
879147072Sbrooks		type = "DHCPNACK";
880147072Sbrooks		break;
881147072Sbrooks	case DHCPACK:
882147072Sbrooks		handler = dhcpack;
883147072Sbrooks		type = "DHCPACK";
884147072Sbrooks		break;
885147072Sbrooks	default:
886147072Sbrooks		return;
887147072Sbrooks	}
888147072Sbrooks
889147072Sbrooks	/* If there's a reject list, make sure this packet's sender isn't
890147072Sbrooks	   on it. */
891147072Sbrooks	for (ap = packet->interface->client->config->reject_list;
892147072Sbrooks	    ap; ap = ap->next) {
893147072Sbrooks		if (addr_eq(packet->client_addr, ap->addr)) {
894147072Sbrooks			note("%s from %s rejected.", type, piaddr(ap->addr));
895147072Sbrooks			return;
896147072Sbrooks		}
897147072Sbrooks	}
898147072Sbrooks	(*handler)(packet);
899147072Sbrooks}
900147072Sbrooks
901147072Sbrooksvoid
902147072Sbrooksdhcpoffer(struct packet *packet)
903147072Sbrooks{
904147072Sbrooks	struct interface_info *ip = packet->interface;
905147072Sbrooks	struct client_lease *lease, *lp;
906147072Sbrooks	int i;
907147072Sbrooks	int arp_timeout_needed, stop_selecting;
908147072Sbrooks	char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
909147072Sbrooks	    "DHCPOFFER" : "BOOTREPLY";
910147072Sbrooks
911147072Sbrooks	/* If we're not receptive to an offer right now, or if the offer
912147072Sbrooks	   has an unrecognizable transaction id, then just drop it. */
913147072Sbrooks	if (ip->client->state != S_SELECTING ||
914147072Sbrooks	    packet->interface->client->xid != packet->raw->xid ||
915147072Sbrooks	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
916147072Sbrooks	    (memcmp(packet->interface->hw_address.haddr,
917147072Sbrooks	    packet->raw->chaddr, packet->raw->hlen)))
918147072Sbrooks		return;
919147072Sbrooks
920147072Sbrooks	note("%s from %s", name, piaddr(packet->client_addr));
921147072Sbrooks
922147072Sbrooks
923147072Sbrooks	/* If this lease doesn't supply the minimum required parameters,
924147072Sbrooks	   blow it off. */
925147072Sbrooks	for (i = 0; ip->client->config->required_options[i]; i++) {
926147072Sbrooks		if (!packet->options[ip->client->config->
927147072Sbrooks		    required_options[i]].len) {
928147072Sbrooks			note("%s isn't satisfactory.", name);
929147072Sbrooks			return;
930147072Sbrooks		}
931147072Sbrooks	}
932147072Sbrooks
933147072Sbrooks	/* If we've already seen this lease, don't record it again. */
934147072Sbrooks	for (lease = ip->client->offered_leases;
935147072Sbrooks	    lease; lease = lease->next) {
936147072Sbrooks		if (lease->address.len == sizeof(packet->raw->yiaddr) &&
937147072Sbrooks		    !memcmp(lease->address.iabuf,
938147072Sbrooks		    &packet->raw->yiaddr, lease->address.len)) {
939147072Sbrooks			debug("%s already seen.", name);
940147072Sbrooks			return;
941147072Sbrooks		}
942147072Sbrooks	}
943147072Sbrooks
944147072Sbrooks	lease = packet_to_lease(packet);
945147072Sbrooks	if (!lease) {
946147072Sbrooks		note("packet_to_lease failed.");
947147072Sbrooks		return;
948147072Sbrooks	}
949147072Sbrooks
950147072Sbrooks	/* If this lease was acquired through a BOOTREPLY, record that
951147072Sbrooks	   fact. */
952147072Sbrooks	if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
953147072Sbrooks		lease->is_bootp = 1;
954147072Sbrooks
955147072Sbrooks	/* Record the medium under which this lease was offered. */
956147072Sbrooks	lease->medium = ip->client->medium;
957147072Sbrooks
958147072Sbrooks	/* Send out an ARP Request for the offered IP address. */
959147072Sbrooks	script_init("ARPSEND", lease->medium);
960147072Sbrooks	script_write_params("check_", lease);
961147072Sbrooks	/* If the script can't send an ARP request without waiting,
962147072Sbrooks	   we'll be waiting when we do the ARPCHECK, so don't wait now. */
963147072Sbrooks	if (script_go())
964147072Sbrooks		arp_timeout_needed = 0;
965147072Sbrooks	else
966147072Sbrooks		arp_timeout_needed = 2;
967147072Sbrooks
968147072Sbrooks	/* Figure out when we're supposed to stop selecting. */
969147072Sbrooks	stop_selecting =
970147072Sbrooks	    ip->client->first_sending + ip->client->config->select_interval;
971147072Sbrooks
972147072Sbrooks	/* If this is the lease we asked for, put it at the head of the
973147072Sbrooks	   list, and don't mess with the arp request timeout. */
974147072Sbrooks	if (lease->address.len == ip->client->requested_address.len &&
975147072Sbrooks	    !memcmp(lease->address.iabuf,
976147072Sbrooks	    ip->client->requested_address.iabuf,
977147072Sbrooks	    ip->client->requested_address.len)) {
978147072Sbrooks		lease->next = ip->client->offered_leases;
979147072Sbrooks		ip->client->offered_leases = lease;
980147072Sbrooks	} else {
981147072Sbrooks		/* If we already have an offer, and arping for this
982147072Sbrooks		   offer would take us past the selection timeout,
983147072Sbrooks		   then don't extend the timeout - just hope for the
984147072Sbrooks		   best. */
985147072Sbrooks		if (ip->client->offered_leases &&
986147072Sbrooks		    (cur_time + arp_timeout_needed) > stop_selecting)
987147072Sbrooks			arp_timeout_needed = 0;
988147072Sbrooks
989147072Sbrooks		/* Put the lease at the end of the list. */
990147072Sbrooks		lease->next = NULL;
991147072Sbrooks		if (!ip->client->offered_leases)
992147072Sbrooks			ip->client->offered_leases = lease;
993147072Sbrooks		else {
994147072Sbrooks			for (lp = ip->client->offered_leases; lp->next;
995147072Sbrooks			    lp = lp->next)
996147072Sbrooks				;	/* nothing */
997147072Sbrooks			lp->next = lease;
998147072Sbrooks		}
999147072Sbrooks	}
1000147072Sbrooks
1001147072Sbrooks	/* If we're supposed to stop selecting before we've had time
1002147072Sbrooks	   to wait for the ARPREPLY, add some delay to wait for
1003147072Sbrooks	   the ARPREPLY. */
1004147072Sbrooks	if (stop_selecting - cur_time < arp_timeout_needed)
1005147072Sbrooks		stop_selecting = cur_time + arp_timeout_needed;
1006147072Sbrooks
1007147072Sbrooks	/* If the selecting interval has expired, go immediately to
1008147072Sbrooks	   state_selecting().  Otherwise, time out into
1009147072Sbrooks	   state_selecting at the select interval. */
1010147072Sbrooks	if (stop_selecting <= 0)
1011147072Sbrooks		state_selecting(ip);
1012147072Sbrooks	else {
1013147072Sbrooks		add_timeout(stop_selecting, state_selecting, ip);
1014147072Sbrooks		cancel_timeout(send_discover, ip);
1015147072Sbrooks	}
1016147072Sbrooks}
1017147072Sbrooks
1018147072Sbrooks/* Allocate a client_lease structure and initialize it from the parameters
1019147072Sbrooks   in the specified packet. */
1020147072Sbrooks
1021147072Sbrooksstruct client_lease *
1022147072Sbrookspacket_to_lease(struct packet *packet)
1023147072Sbrooks{
1024147072Sbrooks	struct client_lease *lease;
1025147072Sbrooks	int i;
1026147072Sbrooks
1027147072Sbrooks	lease = malloc(sizeof(struct client_lease));
1028147072Sbrooks
1029147072Sbrooks	if (!lease) {
1030147072Sbrooks		warning("dhcpoffer: no memory to record lease.");
1031147072Sbrooks		return (NULL);
1032147072Sbrooks	}
1033147072Sbrooks
1034147072Sbrooks	memset(lease, 0, sizeof(*lease));
1035147072Sbrooks
1036147072Sbrooks	/* Copy the lease options. */
1037147072Sbrooks	for (i = 0; i < 256; i++) {
1038147072Sbrooks		if (packet->options[i].len) {
1039147072Sbrooks			lease->options[i].data =
1040147072Sbrooks			    malloc(packet->options[i].len + 1);
1041147072Sbrooks			if (!lease->options[i].data) {
1042147072Sbrooks				warning("dhcpoffer: no memory for option %d", i);
1043147072Sbrooks				free_client_lease(lease);
1044147072Sbrooks				return (NULL);
1045147072Sbrooks			} else {
1046147072Sbrooks				memcpy(lease->options[i].data,
1047147072Sbrooks				    packet->options[i].data,
1048147072Sbrooks				    packet->options[i].len);
1049147072Sbrooks				lease->options[i].len =
1050147072Sbrooks				    packet->options[i].len;
1051147072Sbrooks				lease->options[i].data[lease->options[i].len] =
1052147072Sbrooks				    0;
1053147072Sbrooks			}
1054147072Sbrooks			if (!check_option(lease,i)) {
1055147072Sbrooks				/* ignore a bogus lease offer */
1056147072Sbrooks				warning("Invalid lease option - ignoring offer");
1057147072Sbrooks				free_client_lease(lease);
1058147072Sbrooks				return (NULL);
1059147072Sbrooks			}
1060147072Sbrooks		}
1061147072Sbrooks	}
1062147072Sbrooks
1063147072Sbrooks	lease->address.len = sizeof(packet->raw->yiaddr);
1064147072Sbrooks	memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
1065147072Sbrooks
1066148465Sbrooks	/* If the server name was filled out, copy it.
1067148465Sbrooks	   Do not attempt to validate the server name as a host name.
1068148465Sbrooks	   RFC 2131 merely states that sname is NUL-terminated (which do
1069148465Sbrooks	   do not assume) and that it is the server's host name.  Since
1070148465Sbrooks	   the ISC client and server allow arbitrary characters, we do
1071148465Sbrooks	   as well. */
1072147072Sbrooks	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
1073147072Sbrooks	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
1074147072Sbrooks	    packet->raw->sname[0]) {
1075147072Sbrooks		lease->server_name = malloc(DHCP_SNAME_LEN + 1);
1076147072Sbrooks		if (!lease->server_name) {
1077147072Sbrooks			warning("dhcpoffer: no memory for server name.");
1078147072Sbrooks			free_client_lease(lease);
1079147072Sbrooks			return (NULL);
1080147072Sbrooks		}
1081147072Sbrooks		memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
1082147072Sbrooks		lease->server_name[DHCP_SNAME_LEN]='\0';
1083147072Sbrooks	}
1084147072Sbrooks
1085147072Sbrooks	/* Ditto for the filename. */
1086147072Sbrooks	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
1087147072Sbrooks	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
1088147072Sbrooks	    packet->raw->file[0]) {
1089147072Sbrooks		/* Don't count on the NUL terminator. */
1090147072Sbrooks		lease->filename = malloc(DHCP_FILE_LEN + 1);
1091147072Sbrooks		if (!lease->filename) {
1092147072Sbrooks			warning("dhcpoffer: no memory for filename.");
1093147072Sbrooks			free_client_lease(lease);
1094147072Sbrooks			return (NULL);
1095147072Sbrooks		}
1096147072Sbrooks		memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
1097147072Sbrooks		lease->filename[DHCP_FILE_LEN]='\0';
1098147072Sbrooks	}
1099147072Sbrooks	return lease;
1100147072Sbrooks}
1101147072Sbrooks
1102147072Sbrooksvoid
1103147072Sbrooksdhcpnak(struct packet *packet)
1104147072Sbrooks{
1105147072Sbrooks	struct interface_info *ip = packet->interface;
1106147072Sbrooks
1107147072Sbrooks	/* If we're not receptive to an offer right now, or if the offer
1108147072Sbrooks	   has an unrecognizable transaction id, then just drop it. */
1109147072Sbrooks	if (packet->interface->client->xid != packet->raw->xid ||
1110147072Sbrooks	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
1111147072Sbrooks	    (memcmp(packet->interface->hw_address.haddr,
1112147072Sbrooks	    packet->raw->chaddr, packet->raw->hlen)))
1113147072Sbrooks		return;
1114147072Sbrooks
1115147072Sbrooks	if (ip->client->state != S_REBOOTING &&
1116147072Sbrooks	    ip->client->state != S_REQUESTING &&
1117147072Sbrooks	    ip->client->state != S_RENEWING &&
1118147072Sbrooks	    ip->client->state != S_REBINDING)
1119147072Sbrooks		return;
1120147072Sbrooks
1121147072Sbrooks	note("DHCPNAK from %s", piaddr(packet->client_addr));
1122147072Sbrooks
1123147072Sbrooks	if (!ip->client->active) {
1124147072Sbrooks		note("DHCPNAK with no active lease.\n");
1125147072Sbrooks		return;
1126147072Sbrooks	}
1127147072Sbrooks
1128147072Sbrooks	free_client_lease(ip->client->active);
1129147072Sbrooks	ip->client->active = NULL;
1130147072Sbrooks
1131147072Sbrooks	/* Stop sending DHCPREQUEST packets... */
1132147072Sbrooks	cancel_timeout(send_request, ip);
1133147072Sbrooks
1134147072Sbrooks	ip->client->state = S_INIT;
1135147072Sbrooks	state_init(ip);
1136147072Sbrooks}
1137147072Sbrooks
1138147072Sbrooks/* Send out a DHCPDISCOVER packet, and set a timeout to send out another
1139147072Sbrooks   one after the right interval has expired.  If we don't get an offer by
1140147072Sbrooks   the time we reach the panic interval, call the panic function. */
1141147072Sbrooks
1142147072Sbrooksvoid
1143147072Sbrookssend_discover(void *ipp)
1144147072Sbrooks{
1145147072Sbrooks	struct interface_info *ip = ipp;
1146147072Sbrooks	int interval, increase = 1;
1147147072Sbrooks
1148147072Sbrooks	/* Figure out how long it's been since we started transmitting. */
1149147072Sbrooks	interval = cur_time - ip->client->first_sending;
1150147072Sbrooks
1151147072Sbrooks	/* If we're past the panic timeout, call the script and tell it
1152147072Sbrooks	   we haven't found anything for this interface yet. */
1153147072Sbrooks	if (interval > ip->client->config->timeout) {
1154147072Sbrooks		state_panic(ip);
1155147072Sbrooks		return;
1156147072Sbrooks	}
1157147072Sbrooks
1158147072Sbrooks	/* If we're selecting media, try the whole list before doing
1159147072Sbrooks	   the exponential backoff, but if we've already received an
1160147072Sbrooks	   offer, stop looping, because we obviously have it right. */
1161147072Sbrooks	if (!ip->client->offered_leases &&
1162147072Sbrooks	    ip->client->config->media) {
1163147072Sbrooks		int fail = 0;
1164147072Sbrooksagain:
1165147072Sbrooks		if (ip->client->medium) {
1166147072Sbrooks			ip->client->medium = ip->client->medium->next;
1167147072Sbrooks			increase = 0;
1168147072Sbrooks		}
1169147072Sbrooks		if (!ip->client->medium) {
1170147072Sbrooks			if (fail)
1171147072Sbrooks				error("No valid media types for %s!", ip->name);
1172147072Sbrooks			ip->client->medium = ip->client->config->media;
1173147072Sbrooks			increase = 1;
1174147072Sbrooks		}
1175147072Sbrooks
1176147072Sbrooks		note("Trying medium \"%s\" %d", ip->client->medium->string,
1177147072Sbrooks		    increase);
1178147072Sbrooks		script_init("MEDIUM", ip->client->medium);
1179147072Sbrooks		if (script_go())
1180147072Sbrooks			goto again;
1181147072Sbrooks	}
1182147072Sbrooks
1183147072Sbrooks	/*
1184147072Sbrooks	 * If we're supposed to increase the interval, do so.  If it's
1185147072Sbrooks	 * currently zero (i.e., we haven't sent any packets yet), set
1186147072Sbrooks	 * it to one; otherwise, add to it a random number between zero
1187147072Sbrooks	 * and two times itself.  On average, this means that it will
1188147072Sbrooks	 * double with every transmission.
1189147072Sbrooks	 */
1190147072Sbrooks	if (increase) {
1191147072Sbrooks		if (!ip->client->interval)
1192147072Sbrooks			ip->client->interval =
1193147072Sbrooks			    ip->client->config->initial_interval;
1194147072Sbrooks		else {
1195147072Sbrooks			ip->client->interval += (arc4random() >> 2) %
1196147072Sbrooks			    (2 * ip->client->interval);
1197147072Sbrooks		}
1198147072Sbrooks
1199147072Sbrooks		/* Don't backoff past cutoff. */
1200147072Sbrooks		if (ip->client->interval >
1201147072Sbrooks		    ip->client->config->backoff_cutoff)
1202147072Sbrooks			ip->client->interval =
1203147072Sbrooks				((ip->client->config->backoff_cutoff / 2)
1204147072Sbrooks				 + ((arc4random() >> 2) %
1205147072Sbrooks				    ip->client->config->backoff_cutoff));
1206147072Sbrooks	} else if (!ip->client->interval)
1207147072Sbrooks		ip->client->interval =
1208147072Sbrooks			ip->client->config->initial_interval;
1209147072Sbrooks
1210147072Sbrooks	/* If the backoff would take us to the panic timeout, just use that
1211147072Sbrooks	   as the interval. */
1212147072Sbrooks	if (cur_time + ip->client->interval >
1213147072Sbrooks	    ip->client->first_sending + ip->client->config->timeout)
1214147072Sbrooks		ip->client->interval =
1215147072Sbrooks			(ip->client->first_sending +
1216147072Sbrooks			 ip->client->config->timeout) - cur_time + 1;
1217147072Sbrooks
1218147072Sbrooks	/* Record the number of seconds since we started sending. */
1219147072Sbrooks	if (interval < 65536)
1220147072Sbrooks		ip->client->packet.secs = htons(interval);
1221147072Sbrooks	else
1222147072Sbrooks		ip->client->packet.secs = htons(65535);
1223147072Sbrooks	ip->client->secs = ip->client->packet.secs;
1224147072Sbrooks
1225147072Sbrooks	note("DHCPDISCOVER on %s to %s port %d interval %d",
1226147072Sbrooks	    ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
1227147106Sbrooks	    ntohs(sockaddr_broadcast.sin_port),
1228147106Sbrooks	    (int)ip->client->interval);
1229147072Sbrooks
1230147072Sbrooks	/* Send out a packet. */
1231147072Sbrooks	(void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
1232147072Sbrooks	    inaddr_any, &sockaddr_broadcast, NULL);
1233147072Sbrooks
1234147072Sbrooks	add_timeout(cur_time + ip->client->interval, send_discover, ip);
1235147072Sbrooks}
1236147072Sbrooks
1237147072Sbrooks/*
1238147072Sbrooks * state_panic gets called if we haven't received any offers in a preset
1239147072Sbrooks * amount of time.   When this happens, we try to use existing leases
1240147072Sbrooks * that haven't yet expired, and failing that, we call the client script
1241147072Sbrooks * and hope it can do something.
1242147072Sbrooks */
1243147072Sbrooksvoid
1244147072Sbrooksstate_panic(void *ipp)
1245147072Sbrooks{
1246147072Sbrooks	struct interface_info *ip = ipp;
1247147072Sbrooks	struct client_lease *loop = ip->client->active;
1248147072Sbrooks	struct client_lease *lp;
1249147072Sbrooks
1250147072Sbrooks	note("No DHCPOFFERS received.");
1251147072Sbrooks
1252147072Sbrooks	/* We may not have an active lease, but we may have some
1253147072Sbrooks	   predefined leases that we can try. */
1254147072Sbrooks	if (!ip->client->active && ip->client->leases)
1255147072Sbrooks		goto activate_next;
1256147072Sbrooks
1257147072Sbrooks	/* Run through the list of leases and see if one can be used. */
1258147072Sbrooks	while (ip->client->active) {
1259147072Sbrooks		if (ip->client->active->expiry > cur_time) {
1260147072Sbrooks			note("Trying recorded lease %s",
1261147072Sbrooks			    piaddr(ip->client->active->address));
1262147072Sbrooks			/* Run the client script with the existing
1263147072Sbrooks			   parameters. */
1264147072Sbrooks			script_init("TIMEOUT",
1265147072Sbrooks			    ip->client->active->medium);
1266147072Sbrooks			script_write_params("new_", ip->client->active);
1267147072Sbrooks			if (ip->client->alias)
1268147072Sbrooks				script_write_params("alias_",
1269147072Sbrooks				    ip->client->alias);
1270147072Sbrooks
1271147072Sbrooks			/* If the old lease is still good and doesn't
1272147072Sbrooks			   yet need renewal, go into BOUND state and
1273147072Sbrooks			   timeout at the renewal time. */
1274147072Sbrooks			if (!script_go()) {
1275147072Sbrooks				if (cur_time <
1276147072Sbrooks				    ip->client->active->renewal) {
1277147072Sbrooks					ip->client->state = S_BOUND;
1278147072Sbrooks					note("bound: renewal in %d seconds.",
1279147106Sbrooks					    (int)(ip->client->active->renewal -
1280147106Sbrooks					    cur_time));
1281147072Sbrooks					add_timeout(
1282147072Sbrooks					    ip->client->active->renewal,
1283147072Sbrooks					    state_bound, ip);
1284147072Sbrooks				} else {
1285147072Sbrooks					ip->client->state = S_BOUND;
1286147072Sbrooks					note("bound: immediate renewal.");
1287147072Sbrooks					state_bound(ip);
1288147072Sbrooks				}
1289147072Sbrooks				reinitialize_interfaces();
1290147072Sbrooks				go_daemon();
1291147072Sbrooks				return;
1292147072Sbrooks			}
1293147072Sbrooks		}
1294147072Sbrooks
1295147072Sbrooks		/* If there are no other leases, give up. */
1296147072Sbrooks		if (!ip->client->leases) {
1297147072Sbrooks			ip->client->leases = ip->client->active;
1298147072Sbrooks			ip->client->active = NULL;
1299147072Sbrooks			break;
1300147072Sbrooks		}
1301147072Sbrooks
1302147072Sbrooksactivate_next:
1303147072Sbrooks		/* Otherwise, put the active lease at the end of the
1304147072Sbrooks		   lease list, and try another lease.. */
1305147072Sbrooks		for (lp = ip->client->leases; lp->next; lp = lp->next)
1306147072Sbrooks			;
1307147072Sbrooks		lp->next = ip->client->active;
1308147072Sbrooks		if (lp->next)
1309147072Sbrooks			lp->next->next = NULL;
1310147072Sbrooks		ip->client->active = ip->client->leases;
1311147072Sbrooks		ip->client->leases = ip->client->leases->next;
1312147072Sbrooks
1313147072Sbrooks		/* If we already tried this lease, we've exhausted the
1314147072Sbrooks		   set of leases, so we might as well give up for
1315147072Sbrooks		   now. */
1316147072Sbrooks		if (ip->client->active == loop)
1317147072Sbrooks			break;
1318147072Sbrooks		else if (!loop)
1319147072Sbrooks			loop = ip->client->active;
1320147072Sbrooks	}
1321147072Sbrooks
1322147072Sbrooks	/* No leases were available, or what was available didn't work, so
1323147072Sbrooks	   tell the shell script that we failed to allocate an address,
1324147072Sbrooks	   and try again later. */
1325147072Sbrooks	note("No working leases in persistent database - sleeping.\n");
1326147072Sbrooks	script_init("FAIL", NULL);
1327147072Sbrooks	if (ip->client->alias)
1328147072Sbrooks		script_write_params("alias_", ip->client->alias);
1329147072Sbrooks	script_go();
1330147072Sbrooks	ip->client->state = S_INIT;
1331147072Sbrooks	add_timeout(cur_time + ip->client->config->retry_interval, state_init,
1332147072Sbrooks	    ip);
1333147072Sbrooks	go_daemon();
1334147072Sbrooks}
1335147072Sbrooks
1336147072Sbrooksvoid
1337147072Sbrookssend_request(void *ipp)
1338147072Sbrooks{
1339147072Sbrooks	struct interface_info *ip = ipp;
1340147072Sbrooks	struct sockaddr_in destination;
1341147072Sbrooks	struct in_addr from;
1342147072Sbrooks	int interval;
1343147072Sbrooks
1344147072Sbrooks	/* Figure out how long it's been since we started transmitting. */
1345147072Sbrooks	interval = cur_time - ip->client->first_sending;
1346147072Sbrooks
1347147072Sbrooks	/* If we're in the INIT-REBOOT or REQUESTING state and we're
1348147072Sbrooks	   past the reboot timeout, go to INIT and see if we can
1349147072Sbrooks	   DISCOVER an address... */
1350147072Sbrooks	/* XXX In the INIT-REBOOT state, if we don't get an ACK, it
1351147072Sbrooks	   means either that we're on a network with no DHCP server,
1352147072Sbrooks	   or that our server is down.  In the latter case, assuming
1353147072Sbrooks	   that there is a backup DHCP server, DHCPDISCOVER will get
1354147072Sbrooks	   us a new address, but we could also have successfully
1355147072Sbrooks	   reused our old address.  In the former case, we're hosed
1356147072Sbrooks	   anyway.  This is not a win-prone situation. */
1357147072Sbrooks	if ((ip->client->state == S_REBOOTING ||
1358147072Sbrooks	    ip->client->state == S_REQUESTING) &&
1359147072Sbrooks	    interval > ip->client->config->reboot_timeout) {
1360147072Sbrookscancel:
1361147072Sbrooks		ip->client->state = S_INIT;
1362147072Sbrooks		cancel_timeout(send_request, ip);
1363147072Sbrooks		state_init(ip);
1364147072Sbrooks		return;
1365147072Sbrooks	}
1366147072Sbrooks
1367147072Sbrooks	/* If we're in the reboot state, make sure the media is set up
1368147072Sbrooks	   correctly. */
1369147072Sbrooks	if (ip->client->state == S_REBOOTING &&
1370147072Sbrooks	    !ip->client->medium &&
1371147072Sbrooks	    ip->client->active->medium ) {
1372147072Sbrooks		script_init("MEDIUM", ip->client->active->medium);
1373147072Sbrooks
1374147072Sbrooks		/* If the medium we chose won't fly, go to INIT state. */
1375147072Sbrooks		if (script_go())
1376147072Sbrooks			goto cancel;
1377147072Sbrooks
1378147072Sbrooks		/* Record the medium. */
1379147072Sbrooks		ip->client->medium = ip->client->active->medium;
1380147072Sbrooks	}
1381147072Sbrooks
1382147072Sbrooks	/* If the lease has expired, relinquish the address and go back
1383147072Sbrooks	   to the INIT state. */
1384147072Sbrooks	if (ip->client->state != S_REQUESTING &&
1385147072Sbrooks	    cur_time > ip->client->active->expiry) {
1386147072Sbrooks		/* Run the client script with the new parameters. */
1387147072Sbrooks		script_init("EXPIRE", NULL);
1388147072Sbrooks		script_write_params("old_", ip->client->active);
1389147072Sbrooks		if (ip->client->alias)
1390147072Sbrooks			script_write_params("alias_", ip->client->alias);
1391147072Sbrooks		script_go();
1392147072Sbrooks
1393147072Sbrooks		/* Now do a preinit on the interface so that we can
1394147072Sbrooks		   discover a new address. */
1395147072Sbrooks		script_init("PREINIT", NULL);
1396147072Sbrooks		if (ip->client->alias)
1397147072Sbrooks			script_write_params("alias_", ip->client->alias);
1398147072Sbrooks		script_go();
1399147072Sbrooks
1400147072Sbrooks		ip->client->state = S_INIT;
1401147072Sbrooks		state_init(ip);
1402147072Sbrooks		return;
1403147072Sbrooks	}
1404147072Sbrooks
1405147072Sbrooks	/* Do the exponential backoff... */
1406147072Sbrooks	if (!ip->client->interval)
1407147072Sbrooks		ip->client->interval = ip->client->config->initial_interval;
1408147072Sbrooks	else
1409147072Sbrooks		ip->client->interval += ((arc4random() >> 2) %
1410147072Sbrooks		    (2 * ip->client->interval));
1411147072Sbrooks
1412147072Sbrooks	/* Don't backoff past cutoff. */
1413147072Sbrooks	if (ip->client->interval >
1414147072Sbrooks	    ip->client->config->backoff_cutoff)
1415147072Sbrooks		ip->client->interval =
1416147072Sbrooks		    ((ip->client->config->backoff_cutoff / 2) +
1417147072Sbrooks		    ((arc4random() >> 2) % ip->client->interval));
1418147072Sbrooks
1419147072Sbrooks	/* If the backoff would take us to the expiry time, just set the
1420147072Sbrooks	   timeout to the expiry time. */
1421147072Sbrooks	if (ip->client->state != S_REQUESTING &&
1422147072Sbrooks	    cur_time + ip->client->interval >
1423147072Sbrooks	    ip->client->active->expiry)
1424147072Sbrooks		ip->client->interval =
1425147072Sbrooks		    ip->client->active->expiry - cur_time + 1;
1426147072Sbrooks
1427147072Sbrooks	/* If the lease T2 time has elapsed, or if we're not yet bound,
1428147072Sbrooks	   broadcast the DHCPREQUEST rather than unicasting. */
1429147072Sbrooks	memset(&destination, 0, sizeof(destination));
1430147072Sbrooks	if (ip->client->state == S_REQUESTING ||
1431147072Sbrooks	    ip->client->state == S_REBOOTING ||
1432147072Sbrooks	    cur_time > ip->client->active->rebind)
1433147072Sbrooks		destination.sin_addr.s_addr = INADDR_BROADCAST;
1434147072Sbrooks	else
1435147072Sbrooks		memcpy(&destination.sin_addr.s_addr,
1436147072Sbrooks		    ip->client->destination.iabuf,
1437147072Sbrooks		    sizeof(destination.sin_addr.s_addr));
1438147072Sbrooks	destination.sin_port = htons(REMOTE_PORT);
1439147072Sbrooks	destination.sin_family = AF_INET;
1440147072Sbrooks	destination.sin_len = sizeof(destination);
1441147072Sbrooks
1442147072Sbrooks	if (ip->client->state != S_REQUESTING)
1443147072Sbrooks		memcpy(&from, ip->client->active->address.iabuf,
1444147072Sbrooks		    sizeof(from));
1445147072Sbrooks	else
1446147072Sbrooks		from.s_addr = INADDR_ANY;
1447147072Sbrooks
1448147072Sbrooks	/* Record the number of seconds since we started sending. */
1449147072Sbrooks	if (ip->client->state == S_REQUESTING)
1450147072Sbrooks		ip->client->packet.secs = ip->client->secs;
1451147072Sbrooks	else {
1452147072Sbrooks		if (interval < 65536)
1453147072Sbrooks			ip->client->packet.secs = htons(interval);
1454147072Sbrooks		else
1455147072Sbrooks			ip->client->packet.secs = htons(65535);
1456147072Sbrooks	}
1457147072Sbrooks
1458147072Sbrooks	note("DHCPREQUEST on %s to %s port %d", ip->name,
1459147072Sbrooks	    inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
1460147072Sbrooks
1461147072Sbrooks	/* Send out a packet. */
1462147072Sbrooks	(void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
1463147072Sbrooks	    from, &destination, NULL);
1464147072Sbrooks
1465147072Sbrooks	add_timeout(cur_time + ip->client->interval, send_request, ip);
1466147072Sbrooks}
1467147072Sbrooks
1468147072Sbrooksvoid
1469147072Sbrookssend_decline(void *ipp)
1470147072Sbrooks{
1471147072Sbrooks	struct interface_info *ip = ipp;
1472147072Sbrooks
1473147072Sbrooks	note("DHCPDECLINE on %s to %s port %d", ip->name,
1474147072Sbrooks	    inet_ntoa(sockaddr_broadcast.sin_addr),
1475147072Sbrooks	    ntohs(sockaddr_broadcast.sin_port));
1476147072Sbrooks
1477147072Sbrooks	/* Send out a packet. */
1478147072Sbrooks	(void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
1479147072Sbrooks	    inaddr_any, &sockaddr_broadcast, NULL);
1480147072Sbrooks}
1481147072Sbrooks
1482147072Sbrooksvoid
1483147072Sbrooksmake_discover(struct interface_info *ip, struct client_lease *lease)
1484147072Sbrooks{
1485147072Sbrooks	unsigned char discover = DHCPDISCOVER;
1486147072Sbrooks	struct tree_cache *options[256];
1487147072Sbrooks	struct tree_cache option_elements[256];
1488147072Sbrooks	int i;
1489147072Sbrooks
1490147072Sbrooks	memset(option_elements, 0, sizeof(option_elements));
1491147072Sbrooks	memset(options, 0, sizeof(options));
1492147072Sbrooks	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1493147072Sbrooks
1494147072Sbrooks	/* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
1495147072Sbrooks	i = DHO_DHCP_MESSAGE_TYPE;
1496147072Sbrooks	options[i] = &option_elements[i];
1497147072Sbrooks	options[i]->value = &discover;
1498147072Sbrooks	options[i]->len = sizeof(discover);
1499147072Sbrooks	options[i]->buf_size = sizeof(discover);
1500147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1501147072Sbrooks
1502147072Sbrooks	/* Request the options we want */
1503147072Sbrooks	i  = DHO_DHCP_PARAMETER_REQUEST_LIST;
1504147072Sbrooks	options[i] = &option_elements[i];
1505147072Sbrooks	options[i]->value = ip->client->config->requested_options;
1506147072Sbrooks	options[i]->len = ip->client->config->requested_option_count;
1507147072Sbrooks	options[i]->buf_size =
1508147072Sbrooks		ip->client->config->requested_option_count;
1509147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1510147072Sbrooks
1511147072Sbrooks	/* If we had an address, try to get it again. */
1512147072Sbrooks	if (lease) {
1513147072Sbrooks		ip->client->requested_address = lease->address;
1514147072Sbrooks		i = DHO_DHCP_REQUESTED_ADDRESS;
1515147072Sbrooks		options[i] = &option_elements[i];
1516147072Sbrooks		options[i]->value = lease->address.iabuf;
1517147072Sbrooks		options[i]->len = lease->address.len;
1518147072Sbrooks		options[i]->buf_size = lease->address.len;
1519147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1520147072Sbrooks	} else
1521147072Sbrooks		ip->client->requested_address.len = 0;
1522147072Sbrooks
1523147072Sbrooks	/* Send any options requested in the config file. */
1524147072Sbrooks	for (i = 0; i < 256; i++)
1525147072Sbrooks		if (!options[i] &&
1526147072Sbrooks		    ip->client->config->send_options[i].data) {
1527147072Sbrooks			options[i] = &option_elements[i];
1528147072Sbrooks			options[i]->value =
1529147072Sbrooks			    ip->client->config->send_options[i].data;
1530147072Sbrooks			options[i]->len =
1531147072Sbrooks			    ip->client->config->send_options[i].len;
1532147072Sbrooks			options[i]->buf_size =
1533147072Sbrooks			    ip->client->config->send_options[i].len;
1534147072Sbrooks			options[i]->timeout = 0xFFFFFFFF;
1535147072Sbrooks		}
1536158353Sbrooks
1537158353Sbrooks	/* send host name if not set via config file. */
1538158353Sbrooks	char hostname[_POSIX_HOST_NAME_MAX+1];
1539158353Sbrooks	if (!options[DHO_HOST_NAME]) {
1540158353Sbrooks		if (gethostname(hostname, sizeof(hostname)) == 0) {
1541158353Sbrooks			size_t len;
1542158353Sbrooks			char* posDot = strchr(hostname, '.');
1543158353Sbrooks			if (posDot != NULL)
1544158353Sbrooks				len = posDot - hostname;
1545158353Sbrooks			else
1546158353Sbrooks				len = strlen(hostname);
1547158353Sbrooks			options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME];
1548158353Sbrooks			options[DHO_HOST_NAME]->value = hostname;
1549158353Sbrooks			options[DHO_HOST_NAME]->len = len;
1550158353Sbrooks			options[DHO_HOST_NAME]->buf_size = len;
1551158353Sbrooks			options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF;
1552158353Sbrooks		}
1553158353Sbrooks	}
1554147072Sbrooks
1555158353Sbrooks	/* set unique client identifier */
1556158353Sbrooks	char client_ident[sizeof(struct hardware)];
1557158353Sbrooks	if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
1558158353Sbrooks		int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
1559158353Sbrooks				ip->hw_address.hlen : sizeof(client_ident)-1;
1560158353Sbrooks		client_ident[0] = ip->hw_address.htype;
1561158353Sbrooks		memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
1562158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
1563158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
1564158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
1565158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1;
1566158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF;
1567158353Sbrooks	}
1568158353Sbrooks
1569147072Sbrooks	/* Set up the option buffer... */
1570147072Sbrooks	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1571147072Sbrooks	    options, 0, 0, 0, NULL, 0);
1572147072Sbrooks	if (ip->client->packet_length < BOOTP_MIN_LEN)
1573147072Sbrooks		ip->client->packet_length = BOOTP_MIN_LEN;
1574147072Sbrooks
1575147072Sbrooks	ip->client->packet.op = BOOTREQUEST;
1576147072Sbrooks	ip->client->packet.htype = ip->hw_address.htype;
1577147072Sbrooks	ip->client->packet.hlen = ip->hw_address.hlen;
1578147072Sbrooks	ip->client->packet.hops = 0;
1579147072Sbrooks	ip->client->packet.xid = arc4random();
1580147072Sbrooks	ip->client->packet.secs = 0; /* filled in by send_discover. */
1581147072Sbrooks	ip->client->packet.flags = 0;
1582147072Sbrooks
1583147072Sbrooks	memset(&(ip->client->packet.ciaddr),
1584147072Sbrooks	    0, sizeof(ip->client->packet.ciaddr));
1585147072Sbrooks	memset(&(ip->client->packet.yiaddr),
1586147072Sbrooks	    0, sizeof(ip->client->packet.yiaddr));
1587147072Sbrooks	memset(&(ip->client->packet.siaddr),
1588147072Sbrooks	    0, sizeof(ip->client->packet.siaddr));
1589147072Sbrooks	memset(&(ip->client->packet.giaddr),
1590147072Sbrooks	    0, sizeof(ip->client->packet.giaddr));
1591147072Sbrooks	memcpy(ip->client->packet.chaddr,
1592147072Sbrooks	    ip->hw_address.haddr, ip->hw_address.hlen);
1593147072Sbrooks}
1594147072Sbrooks
1595147072Sbrooks
1596147072Sbrooksvoid
1597147072Sbrooksmake_request(struct interface_info *ip, struct client_lease * lease)
1598147072Sbrooks{
1599147072Sbrooks	unsigned char request = DHCPREQUEST;
1600147072Sbrooks	struct tree_cache *options[256];
1601147072Sbrooks	struct tree_cache option_elements[256];
1602147072Sbrooks	int i;
1603147072Sbrooks
1604147072Sbrooks	memset(options, 0, sizeof(options));
1605147072Sbrooks	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1606147072Sbrooks
1607147072Sbrooks	/* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
1608147072Sbrooks	i = DHO_DHCP_MESSAGE_TYPE;
1609147072Sbrooks	options[i] = &option_elements[i];
1610147072Sbrooks	options[i]->value = &request;
1611147072Sbrooks	options[i]->len = sizeof(request);
1612147072Sbrooks	options[i]->buf_size = sizeof(request);
1613147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1614147072Sbrooks
1615147072Sbrooks	/* Request the options we want */
1616147072Sbrooks	i = DHO_DHCP_PARAMETER_REQUEST_LIST;
1617147072Sbrooks	options[i] = &option_elements[i];
1618147072Sbrooks	options[i]->value = ip->client->config->requested_options;
1619147072Sbrooks	options[i]->len = ip->client->config->requested_option_count;
1620147072Sbrooks	options[i]->buf_size =
1621147072Sbrooks		ip->client->config->requested_option_count;
1622147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1623147072Sbrooks
1624147072Sbrooks	/* If we are requesting an address that hasn't yet been assigned
1625147072Sbrooks	   to us, use the DHCP Requested Address option. */
1626147072Sbrooks	if (ip->client->state == S_REQUESTING) {
1627147072Sbrooks		/* Send back the server identifier... */
1628147072Sbrooks		i = DHO_DHCP_SERVER_IDENTIFIER;
1629147072Sbrooks		options[i] = &option_elements[i];
1630147072Sbrooks		options[i]->value = lease->options[i].data;
1631147072Sbrooks		options[i]->len = lease->options[i].len;
1632147072Sbrooks		options[i]->buf_size = lease->options[i].len;
1633147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1634147072Sbrooks	}
1635147072Sbrooks	if (ip->client->state == S_REQUESTING ||
1636147072Sbrooks	    ip->client->state == S_REBOOTING) {
1637147072Sbrooks		ip->client->requested_address = lease->address;
1638147072Sbrooks		i = DHO_DHCP_REQUESTED_ADDRESS;
1639147072Sbrooks		options[i] = &option_elements[i];
1640147072Sbrooks		options[i]->value = lease->address.iabuf;
1641147072Sbrooks		options[i]->len = lease->address.len;
1642147072Sbrooks		options[i]->buf_size = lease->address.len;
1643147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1644147072Sbrooks	} else
1645147072Sbrooks		ip->client->requested_address.len = 0;
1646147072Sbrooks
1647147072Sbrooks	/* Send any options requested in the config file. */
1648147072Sbrooks	for (i = 0; i < 256; i++)
1649147072Sbrooks		if (!options[i] &&
1650147072Sbrooks		    ip->client->config->send_options[i].data) {
1651147072Sbrooks			options[i] = &option_elements[i];
1652147072Sbrooks			options[i]->value =
1653147072Sbrooks			    ip->client->config->send_options[i].data;
1654147072Sbrooks			options[i]->len =
1655147072Sbrooks			    ip->client->config->send_options[i].len;
1656147072Sbrooks			options[i]->buf_size =
1657147072Sbrooks			    ip->client->config->send_options[i].len;
1658147072Sbrooks			options[i]->timeout = 0xFFFFFFFF;
1659147072Sbrooks		}
1660158353Sbrooks
1661158353Sbrooks	/* send host name if not set via config file. */
1662158353Sbrooks	char hostname[_POSIX_HOST_NAME_MAX+1];
1663158353Sbrooks	if (!options[DHO_HOST_NAME]) {
1664158353Sbrooks		if (gethostname(hostname, sizeof(hostname)) == 0) {
1665158353Sbrooks			size_t len;
1666158353Sbrooks			char* posDot = strchr(hostname, '.');
1667158353Sbrooks			if (posDot != NULL)
1668158353Sbrooks				len = posDot - hostname;
1669158353Sbrooks			else
1670158353Sbrooks				len = strlen(hostname);
1671158353Sbrooks			options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME];
1672158353Sbrooks			options[DHO_HOST_NAME]->value = hostname;
1673158353Sbrooks			options[DHO_HOST_NAME]->len = len;
1674158353Sbrooks			options[DHO_HOST_NAME]->buf_size = len;
1675158353Sbrooks			options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF;
1676158353Sbrooks		}
1677158353Sbrooks	}
1678147072Sbrooks
1679158353Sbrooks	/* set unique client identifier */
1680158353Sbrooks	char client_ident[sizeof(struct hardware)];
1681158353Sbrooks	if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
1682158353Sbrooks		int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
1683158353Sbrooks				ip->hw_address.hlen : sizeof(client_ident)-1;
1684158353Sbrooks		client_ident[0] = ip->hw_address.htype;
1685158353Sbrooks		memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
1686158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
1687158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
1688158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
1689158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1;
1690158353Sbrooks		options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF;
1691158353Sbrooks	}
1692158353Sbrooks
1693147072Sbrooks	/* Set up the option buffer... */
1694147072Sbrooks	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1695147072Sbrooks	    options, 0, 0, 0, NULL, 0);
1696147072Sbrooks	if (ip->client->packet_length < BOOTP_MIN_LEN)
1697147072Sbrooks		ip->client->packet_length = BOOTP_MIN_LEN;
1698147072Sbrooks
1699147072Sbrooks	ip->client->packet.op = BOOTREQUEST;
1700147072Sbrooks	ip->client->packet.htype = ip->hw_address.htype;
1701147072Sbrooks	ip->client->packet.hlen = ip->hw_address.hlen;
1702147072Sbrooks	ip->client->packet.hops = 0;
1703147072Sbrooks	ip->client->packet.xid = ip->client->xid;
1704147072Sbrooks	ip->client->packet.secs = 0; /* Filled in by send_request. */
1705147072Sbrooks
1706147072Sbrooks	/* If we own the address we're requesting, put it in ciaddr;
1707147072Sbrooks	   otherwise set ciaddr to zero. */
1708147072Sbrooks	if (ip->client->state == S_BOUND ||
1709147072Sbrooks	    ip->client->state == S_RENEWING ||
1710147072Sbrooks	    ip->client->state == S_REBINDING) {
1711147072Sbrooks		memcpy(&ip->client->packet.ciaddr,
1712147072Sbrooks		    lease->address.iabuf, lease->address.len);
1713147072Sbrooks		ip->client->packet.flags = 0;
1714147072Sbrooks	} else {
1715147072Sbrooks		memset(&ip->client->packet.ciaddr, 0,
1716147072Sbrooks		    sizeof(ip->client->packet.ciaddr));
1717147072Sbrooks		ip->client->packet.flags = 0;
1718147072Sbrooks	}
1719147072Sbrooks
1720147072Sbrooks	memset(&ip->client->packet.yiaddr, 0,
1721147072Sbrooks	    sizeof(ip->client->packet.yiaddr));
1722147072Sbrooks	memset(&ip->client->packet.siaddr, 0,
1723147072Sbrooks	    sizeof(ip->client->packet.siaddr));
1724147072Sbrooks	memset(&ip->client->packet.giaddr, 0,
1725147072Sbrooks	    sizeof(ip->client->packet.giaddr));
1726147072Sbrooks	memcpy(ip->client->packet.chaddr,
1727147072Sbrooks	    ip->hw_address.haddr, ip->hw_address.hlen);
1728147072Sbrooks}
1729147072Sbrooks
1730147072Sbrooksvoid
1731147072Sbrooksmake_decline(struct interface_info *ip, struct client_lease *lease)
1732147072Sbrooks{
1733147072Sbrooks	struct tree_cache *options[256], message_type_tree;
1734147072Sbrooks	struct tree_cache requested_address_tree;
1735147072Sbrooks	struct tree_cache server_id_tree, client_id_tree;
1736147072Sbrooks	unsigned char decline = DHCPDECLINE;
1737147072Sbrooks	int i;
1738147072Sbrooks
1739147072Sbrooks	memset(options, 0, sizeof(options));
1740147072Sbrooks	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1741147072Sbrooks
1742147072Sbrooks	/* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
1743147072Sbrooks	i = DHO_DHCP_MESSAGE_TYPE;
1744147072Sbrooks	options[i] = &message_type_tree;
1745147072Sbrooks	options[i]->value = &decline;
1746147072Sbrooks	options[i]->len = sizeof(decline);
1747147072Sbrooks	options[i]->buf_size = sizeof(decline);
1748147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1749147072Sbrooks
1750147072Sbrooks	/* Send back the server identifier... */
1751147072Sbrooks	i = DHO_DHCP_SERVER_IDENTIFIER;
1752147072Sbrooks	options[i] = &server_id_tree;
1753147072Sbrooks	options[i]->value = lease->options[i].data;
1754147072Sbrooks	options[i]->len = lease->options[i].len;
1755147072Sbrooks	options[i]->buf_size = lease->options[i].len;
1756147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1757147072Sbrooks
1758147072Sbrooks	/* Send back the address we're declining. */
1759147072Sbrooks	i = DHO_DHCP_REQUESTED_ADDRESS;
1760147072Sbrooks	options[i] = &requested_address_tree;
1761147072Sbrooks	options[i]->value = lease->address.iabuf;
1762147072Sbrooks	options[i]->len = lease->address.len;
1763147072Sbrooks	options[i]->buf_size = lease->address.len;
1764147072Sbrooks	options[i]->timeout = 0xFFFFFFFF;
1765147072Sbrooks
1766147072Sbrooks	/* Send the uid if the user supplied one. */
1767147072Sbrooks	i = DHO_DHCP_CLIENT_IDENTIFIER;
1768147072Sbrooks	if (ip->client->config->send_options[i].len) {
1769147072Sbrooks		options[i] = &client_id_tree;
1770147072Sbrooks		options[i]->value = ip->client->config->send_options[i].data;
1771147072Sbrooks		options[i]->len = ip->client->config->send_options[i].len;
1772147072Sbrooks		options[i]->buf_size = ip->client->config->send_options[i].len;
1773147072Sbrooks		options[i]->timeout = 0xFFFFFFFF;
1774147072Sbrooks	}
1775147072Sbrooks
1776147072Sbrooks
1777147072Sbrooks	/* Set up the option buffer... */
1778147072Sbrooks	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1779147072Sbrooks	    options, 0, 0, 0, NULL, 0);
1780147072Sbrooks	if (ip->client->packet_length < BOOTP_MIN_LEN)
1781147072Sbrooks		ip->client->packet_length = BOOTP_MIN_LEN;
1782147072Sbrooks
1783147072Sbrooks	ip->client->packet.op = BOOTREQUEST;
1784147072Sbrooks	ip->client->packet.htype = ip->hw_address.htype;
1785147072Sbrooks	ip->client->packet.hlen = ip->hw_address.hlen;
1786147072Sbrooks	ip->client->packet.hops = 0;
1787147072Sbrooks	ip->client->packet.xid = ip->client->xid;
1788147072Sbrooks	ip->client->packet.secs = 0; /* Filled in by send_request. */
1789147072Sbrooks	ip->client->packet.flags = 0;
1790147072Sbrooks
1791147072Sbrooks	/* ciaddr must always be zero. */
1792147072Sbrooks	memset(&ip->client->packet.ciaddr, 0,
1793147072Sbrooks	    sizeof(ip->client->packet.ciaddr));
1794147072Sbrooks	memset(&ip->client->packet.yiaddr, 0,
1795147072Sbrooks	    sizeof(ip->client->packet.yiaddr));
1796147072Sbrooks	memset(&ip->client->packet.siaddr, 0,
1797147072Sbrooks	    sizeof(ip->client->packet.siaddr));
1798147072Sbrooks	memset(&ip->client->packet.giaddr, 0,
1799147072Sbrooks	    sizeof(ip->client->packet.giaddr));
1800147072Sbrooks	memcpy(ip->client->packet.chaddr,
1801147072Sbrooks	    ip->hw_address.haddr, ip->hw_address.hlen);
1802147072Sbrooks}
1803147072Sbrooks
1804147072Sbrooksvoid
1805147072Sbrooksfree_client_lease(struct client_lease *lease)
1806147072Sbrooks{
1807147072Sbrooks	int i;
1808147072Sbrooks
1809147072Sbrooks	if (lease->server_name)
1810147072Sbrooks		free(lease->server_name);
1811147072Sbrooks	if (lease->filename)
1812147072Sbrooks		free(lease->filename);
1813147072Sbrooks	for (i = 0; i < 256; i++) {
1814147072Sbrooks		if (lease->options[i].len)
1815147072Sbrooks			free(lease->options[i].data);
1816147072Sbrooks	}
1817147072Sbrooks	free(lease);
1818147072Sbrooks}
1819147072Sbrooks
1820147072SbrooksFILE *leaseFile;
1821147072Sbrooks
1822147072Sbrooksvoid
1823147072Sbrooksrewrite_client_leases(void)
1824147072Sbrooks{
1825147072Sbrooks	struct client_lease *lp;
1826147072Sbrooks
1827147072Sbrooks	if (!leaseFile) {
1828147072Sbrooks		leaseFile = fopen(path_dhclient_db, "w");
1829147072Sbrooks		if (!leaseFile)
1830147072Sbrooks			error("can't create %s: %m", path_dhclient_db);
1831147072Sbrooks	} else {
1832147072Sbrooks		fflush(leaseFile);
1833147072Sbrooks		rewind(leaseFile);
1834147072Sbrooks	}
1835147072Sbrooks
1836147072Sbrooks	for (lp = ifi->client->leases; lp; lp = lp->next)
1837147072Sbrooks		write_client_lease(ifi, lp, 1);
1838147072Sbrooks	if (ifi->client->active)
1839147072Sbrooks		write_client_lease(ifi, ifi->client->active, 1);
1840147072Sbrooks
1841147072Sbrooks	fflush(leaseFile);
1842147072Sbrooks	ftruncate(fileno(leaseFile), ftello(leaseFile));
1843147072Sbrooks	fsync(fileno(leaseFile));
1844147072Sbrooks}
1845147072Sbrooks
1846147072Sbrooksvoid
1847147072Sbrookswrite_client_lease(struct interface_info *ip, struct client_lease *lease,
1848147072Sbrooks    int rewrite)
1849147072Sbrooks{
1850147072Sbrooks	static int leases_written;
1851147072Sbrooks	struct tm *t;
1852147072Sbrooks	int i;
1853147072Sbrooks
1854147072Sbrooks	if (!rewrite) {
1855147072Sbrooks		if (leases_written++ > 20) {
1856147072Sbrooks			rewrite_client_leases();
1857147072Sbrooks			leases_written = 0;
1858147072Sbrooks		}
1859147072Sbrooks	}
1860147072Sbrooks
1861147072Sbrooks	/* If the lease came from the config file, we don't need to stash
1862147072Sbrooks	   a copy in the lease database. */
1863147072Sbrooks	if (lease->is_static)
1864147072Sbrooks		return;
1865147072Sbrooks
1866147072Sbrooks	if (!leaseFile) {	/* XXX */
1867147072Sbrooks		leaseFile = fopen(path_dhclient_db, "w");
1868147072Sbrooks		if (!leaseFile)
1869147072Sbrooks			error("can't create %s: %m", path_dhclient_db);
1870147072Sbrooks	}
1871147072Sbrooks
1872147072Sbrooks	fprintf(leaseFile, "lease {\n");
1873147072Sbrooks	if (lease->is_bootp)
1874147072Sbrooks		fprintf(leaseFile, "  bootp;\n");
1875147072Sbrooks	fprintf(leaseFile, "  interface \"%s\";\n", ip->name);
1876147072Sbrooks	fprintf(leaseFile, "  fixed-address %s;\n", piaddr(lease->address));
1877147072Sbrooks	if (lease->filename)
1878147072Sbrooks		fprintf(leaseFile, "  filename \"%s\";\n", lease->filename);
1879147072Sbrooks	if (lease->server_name)
1880147072Sbrooks		fprintf(leaseFile, "  server-name \"%s\";\n",
1881147072Sbrooks		    lease->server_name);
1882147072Sbrooks	if (lease->medium)
1883147072Sbrooks		fprintf(leaseFile, "  medium \"%s\";\n", lease->medium->string);
1884147072Sbrooks	for (i = 0; i < 256; i++)
1885147072Sbrooks		if (lease->options[i].len)
1886147072Sbrooks			fprintf(leaseFile, "  option %s %s;\n",
1887147072Sbrooks			    dhcp_options[i].name,
1888147072Sbrooks			    pretty_print_option(i, lease->options[i].data,
1889147072Sbrooks			    lease->options[i].len, 1, 1));
1890147072Sbrooks
1891147072Sbrooks	t = gmtime(&lease->renewal);
1892147072Sbrooks	fprintf(leaseFile, "  renew %d %d/%d/%d %02d:%02d:%02d;\n",
1893147072Sbrooks	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1894147072Sbrooks	    t->tm_hour, t->tm_min, t->tm_sec);
1895147072Sbrooks	t = gmtime(&lease->rebind);
1896147072Sbrooks	fprintf(leaseFile, "  rebind %d %d/%d/%d %02d:%02d:%02d;\n",
1897147072Sbrooks	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1898147072Sbrooks	    t->tm_hour, t->tm_min, t->tm_sec);
1899147072Sbrooks	t = gmtime(&lease->expiry);
1900147072Sbrooks	fprintf(leaseFile, "  expire %d %d/%d/%d %02d:%02d:%02d;\n",
1901147072Sbrooks	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1902147072Sbrooks	    t->tm_hour, t->tm_min, t->tm_sec);
1903147072Sbrooks	fprintf(leaseFile, "}\n");
1904147072Sbrooks	fflush(leaseFile);
1905147072Sbrooks}
1906147072Sbrooks
1907147072Sbrooksvoid
1908147072Sbrooksscript_init(char *reason, struct string_list *medium)
1909147072Sbrooks{
1910147072Sbrooks	size_t		 len, mediumlen = 0;
1911147072Sbrooks	struct imsg_hdr	 hdr;
1912147072Sbrooks	struct buf	*buf;
1913147072Sbrooks	int		 errs;
1914147072Sbrooks
1915147072Sbrooks	if (medium != NULL && medium->string != NULL)
1916147072Sbrooks		mediumlen = strlen(medium->string);
1917147072Sbrooks
1918147072Sbrooks	hdr.code = IMSG_SCRIPT_INIT;
1919147072Sbrooks	hdr.len = sizeof(struct imsg_hdr) +
1920147072Sbrooks	    sizeof(size_t) + mediumlen +
1921147072Sbrooks	    sizeof(size_t) + strlen(reason);
1922147072Sbrooks
1923147072Sbrooks	if ((buf = buf_open(hdr.len)) == NULL)
1924147072Sbrooks		error("buf_open: %m");
1925147072Sbrooks
1926147072Sbrooks	errs = 0;
1927147072Sbrooks	errs += buf_add(buf, &hdr, sizeof(hdr));
1928147072Sbrooks	errs += buf_add(buf, &mediumlen, sizeof(mediumlen));
1929147072Sbrooks	if (mediumlen > 0)
1930147072Sbrooks		errs += buf_add(buf, medium->string, mediumlen);
1931147072Sbrooks	len = strlen(reason);
1932147072Sbrooks	errs += buf_add(buf, &len, sizeof(len));
1933147072Sbrooks	errs += buf_add(buf, reason, len);
1934147072Sbrooks
1935147072Sbrooks	if (errs)
1936147072Sbrooks		error("buf_add: %m");
1937147072Sbrooks
1938147072Sbrooks	if (buf_close(privfd, buf) == -1)
1939147072Sbrooks		error("buf_close: %m");
1940147072Sbrooks}
1941147072Sbrooks
1942147072Sbrooksvoid
1943147072Sbrookspriv_script_init(char *reason, char *medium)
1944147072Sbrooks{
1945147072Sbrooks	struct interface_info *ip = ifi;
1946147072Sbrooks
1947147072Sbrooks	if (ip) {
1948147072Sbrooks		ip->client->scriptEnvsize = 100;
1949147072Sbrooks		if (ip->client->scriptEnv == NULL)
1950147072Sbrooks			ip->client->scriptEnv =
1951147072Sbrooks			    malloc(ip->client->scriptEnvsize * sizeof(char *));
1952147072Sbrooks		if (ip->client->scriptEnv == NULL)
1953147072Sbrooks			error("script_init: no memory for environment");
1954147072Sbrooks
1955147072Sbrooks		ip->client->scriptEnv[0] = strdup(CLIENT_PATH);
1956147072Sbrooks		if (ip->client->scriptEnv[0] == NULL)
1957147072Sbrooks			error("script_init: no memory for environment");
1958147072Sbrooks
1959147072Sbrooks		ip->client->scriptEnv[1] = NULL;
1960147072Sbrooks
1961147072Sbrooks		script_set_env(ip->client, "", "interface", ip->name);
1962147072Sbrooks
1963147072Sbrooks		if (medium)
1964147072Sbrooks			script_set_env(ip->client, "", "medium", medium);
1965147072Sbrooks
1966147072Sbrooks		script_set_env(ip->client, "", "reason", reason);
1967147072Sbrooks	}
1968147072Sbrooks}
1969147072Sbrooks
1970147072Sbrooksvoid
1971147072Sbrookspriv_script_write_params(char *prefix, struct client_lease *lease)
1972147072Sbrooks{
1973147072Sbrooks	struct interface_info *ip = ifi;
1974149727Sbrooks	u_int8_t dbuf[1500], *dp = NULL;
1975149727Sbrooks	int i, len;
1976147072Sbrooks	char tbuf[128];
1977147072Sbrooks
1978147072Sbrooks	script_set_env(ip->client, prefix, "ip_address",
1979147072Sbrooks	    piaddr(lease->address));
1980147072Sbrooks
1981149727Sbrooks	if (ip->client->config->default_actions[DHO_SUBNET_MASK] ==
1982149727Sbrooks	    ACTION_SUPERSEDE) {
1983149727Sbrooks		dp = ip->client->config->defaults[DHO_SUBNET_MASK].data;
1984149727Sbrooks		len = ip->client->config->defaults[DHO_SUBNET_MASK].len;
1985149727Sbrooks	} else {
1986149727Sbrooks		dp = lease->options[DHO_SUBNET_MASK].data;
1987149727Sbrooks		len = lease->options[DHO_SUBNET_MASK].len;
1988149727Sbrooks	}
1989149727Sbrooks	if (len && (len < sizeof(lease->address.iabuf))) {
1990147072Sbrooks		struct iaddr netmask, subnet, broadcast;
1991147072Sbrooks
1992149727Sbrooks		memcpy(netmask.iabuf, dp, len);
1993149727Sbrooks		netmask.len = len;
1994147072Sbrooks		subnet = subnet_number(lease->address, netmask);
1995147072Sbrooks		if (subnet.len) {
1996147072Sbrooks			script_set_env(ip->client, prefix, "network_number",
1997147072Sbrooks			    piaddr(subnet));
1998147072Sbrooks			if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
1999147072Sbrooks				broadcast = broadcast_addr(subnet, netmask);
2000147072Sbrooks				if (broadcast.len)
2001147072Sbrooks					script_set_env(ip->client, prefix,
2002147072Sbrooks					    "broadcast_address",
2003147072Sbrooks					    piaddr(broadcast));
2004147072Sbrooks			}
2005147072Sbrooks		}
2006147072Sbrooks	}
2007147072Sbrooks
2008147072Sbrooks	if (lease->filename)
2009147072Sbrooks		script_set_env(ip->client, prefix, "filename", lease->filename);
2010147072Sbrooks	if (lease->server_name)
2011147072Sbrooks		script_set_env(ip->client, prefix, "server_name",
2012147072Sbrooks		    lease->server_name);
2013147072Sbrooks	for (i = 0; i < 256; i++) {
2014149727Sbrooks		len = 0;
2015147072Sbrooks
2016147072Sbrooks		if (ip->client->config->defaults[i].len) {
2017147072Sbrooks			if (lease->options[i].len) {
2018147072Sbrooks				switch (
2019147072Sbrooks				    ip->client->config->default_actions[i]) {
2020147072Sbrooks				case ACTION_DEFAULT:
2021147072Sbrooks					dp = lease->options[i].data;
2022147072Sbrooks					len = lease->options[i].len;
2023147072Sbrooks					break;
2024147072Sbrooks				case ACTION_SUPERSEDE:
2025147072Sbrookssupersede:
2026147072Sbrooks					dp = ip->client->
2027147072Sbrooks						config->defaults[i].data;
2028147072Sbrooks					len = ip->client->
2029147072Sbrooks						config->defaults[i].len;
2030147072Sbrooks					break;
2031147072Sbrooks				case ACTION_PREPEND:
2032147072Sbrooks					len = ip->client->
2033147072Sbrooks					    config->defaults[i].len +
2034147072Sbrooks					    lease->options[i].len;
2035193765Sbrian					if (len >= sizeof(dbuf)) {
2036147072Sbrooks						warning("no space to %s %s",
2037147072Sbrooks						    "prepend option",
2038147072Sbrooks						    dhcp_options[i].name);
2039147072Sbrooks						goto supersede;
2040147072Sbrooks					}
2041147072Sbrooks					dp = dbuf;
2042147072Sbrooks					memcpy(dp,
2043147072Sbrooks						ip->client->
2044147072Sbrooks						config->defaults[i].data,
2045147072Sbrooks						ip->client->
2046147072Sbrooks						config->defaults[i].len);
2047147072Sbrooks					memcpy(dp + ip->client->
2048147072Sbrooks						config->defaults[i].len,
2049147072Sbrooks						lease->options[i].data,
2050147072Sbrooks						lease->options[i].len);
2051147072Sbrooks					dp[len] = '\0';
2052147072Sbrooks					break;
2053147072Sbrooks				case ACTION_APPEND:
2054193765Sbrian					/*
2055193765Sbrian					 * When we append, we assume that we're
2056193765Sbrian					 * appending to text.  Some MS servers
2057193765Sbrian					 * include a NUL byte at the end of
2058193765Sbrian					 * the search string provided.
2059193765Sbrian					 */
2060147072Sbrooks					len = ip->client->
2061147072Sbrooks					    config->defaults[i].len +
2062147072Sbrooks					    lease->options[i].len;
2063193765Sbrian					if (len >= sizeof(dbuf)) {
2064147072Sbrooks						warning("no space to %s %s",
2065147072Sbrooks						    "append option",
2066147072Sbrooks						    dhcp_options[i].name);
2067147072Sbrooks						goto supersede;
2068147072Sbrooks					}
2069193765Sbrian					memcpy(dbuf,
2070147072Sbrooks						lease->options[i].data,
2071147072Sbrooks						lease->options[i].len);
2072193765Sbrian					for (dp = dbuf + lease->options[i].len;
2073193765Sbrian					    dp > dbuf; dp--, len--)
2074193765Sbrian						if (dp[-1] != '\0')
2075193765Sbrian							break;
2076193765Sbrian					memcpy(dp,
2077147072Sbrooks						ip->client->
2078147072Sbrooks						config->defaults[i].data,
2079147072Sbrooks						ip->client->
2080147072Sbrooks						config->defaults[i].len);
2081193765Sbrian					dp = dbuf;
2082147072Sbrooks					dp[len] = '\0';
2083147072Sbrooks				}
2084147072Sbrooks			} else {
2085147072Sbrooks				dp = ip->client->
2086147072Sbrooks					config->defaults[i].data;
2087147072Sbrooks				len = ip->client->
2088147072Sbrooks					config->defaults[i].len;
2089147072Sbrooks			}
2090147072Sbrooks		} else if (lease->options[i].len) {
2091147072Sbrooks			len = lease->options[i].len;
2092147072Sbrooks			dp = lease->options[i].data;
2093147072Sbrooks		} else {
2094147072Sbrooks			len = 0;
2095147072Sbrooks		}
2096147072Sbrooks		if (len) {
2097147072Sbrooks			char name[256];
2098147072Sbrooks
2099147072Sbrooks			if (dhcp_option_ev_name(name, sizeof(name),
2100147072Sbrooks			    &dhcp_options[i]))
2101147072Sbrooks				script_set_env(ip->client, prefix, name,
2102147072Sbrooks				    pretty_print_option(i, dp, len, 0, 0));
2103147072Sbrooks		}
2104147072Sbrooks	}
2105147072Sbrooks	snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
2106147072Sbrooks	script_set_env(ip->client, prefix, "expiry", tbuf);
2107147072Sbrooks}
2108147072Sbrooks
2109147072Sbrooksvoid
2110147072Sbrooksscript_write_params(char *prefix, struct client_lease *lease)
2111147072Sbrooks{
2112147072Sbrooks	size_t		 fn_len = 0, sn_len = 0, pr_len = 0;
2113147072Sbrooks	struct imsg_hdr	 hdr;
2114147072Sbrooks	struct buf	*buf;
2115147072Sbrooks	int		 errs, i;
2116147072Sbrooks
2117147072Sbrooks	if (lease->filename != NULL)
2118147072Sbrooks		fn_len = strlen(lease->filename);
2119147072Sbrooks	if (lease->server_name != NULL)
2120147072Sbrooks		sn_len = strlen(lease->server_name);
2121147072Sbrooks	if (prefix != NULL)
2122147072Sbrooks		pr_len = strlen(prefix);
2123147072Sbrooks
2124147072Sbrooks	hdr.code = IMSG_SCRIPT_WRITE_PARAMS;
2125147072Sbrooks	hdr.len = sizeof(hdr) + sizeof(struct client_lease) +
2126147072Sbrooks	    sizeof(size_t) + fn_len + sizeof(size_t) + sn_len +
2127147072Sbrooks	    sizeof(size_t) + pr_len;
2128147072Sbrooks
2129147072Sbrooks	for (i = 0; i < 256; i++)
2130147072Sbrooks		hdr.len += sizeof(int) + lease->options[i].len;
2131147072Sbrooks
2132147072Sbrooks	scripttime = time(NULL);
2133147072Sbrooks
2134147072Sbrooks	if ((buf = buf_open(hdr.len)) == NULL)
2135147072Sbrooks		error("buf_open: %m");
2136147072Sbrooks
2137147072Sbrooks	errs = 0;
2138147072Sbrooks	errs += buf_add(buf, &hdr, sizeof(hdr));
2139147072Sbrooks	errs += buf_add(buf, lease, sizeof(struct client_lease));
2140147072Sbrooks	errs += buf_add(buf, &fn_len, sizeof(fn_len));
2141147072Sbrooks	errs += buf_add(buf, lease->filename, fn_len);
2142147072Sbrooks	errs += buf_add(buf, &sn_len, sizeof(sn_len));
2143147072Sbrooks	errs += buf_add(buf, lease->server_name, sn_len);
2144147072Sbrooks	errs += buf_add(buf, &pr_len, sizeof(pr_len));
2145147072Sbrooks	errs += buf_add(buf, prefix, pr_len);
2146147072Sbrooks
2147147072Sbrooks	for (i = 0; i < 256; i++) {
2148147072Sbrooks		errs += buf_add(buf, &lease->options[i].len,
2149147072Sbrooks		    sizeof(lease->options[i].len));
2150147072Sbrooks		errs += buf_add(buf, lease->options[i].data,
2151147072Sbrooks		    lease->options[i].len);
2152147072Sbrooks	}
2153147072Sbrooks
2154147072Sbrooks	if (errs)
2155147072Sbrooks		error("buf_add: %m");
2156147072Sbrooks
2157147072Sbrooks	if (buf_close(privfd, buf) == -1)
2158147072Sbrooks		error("buf_close: %m");
2159147072Sbrooks}
2160147072Sbrooks
2161147072Sbrooksint
2162147072Sbrooksscript_go(void)
2163147072Sbrooks{
2164147072Sbrooks	struct imsg_hdr	 hdr;
2165147072Sbrooks	struct buf	*buf;
2166147072Sbrooks	int		 ret;
2167147072Sbrooks
2168147072Sbrooks	hdr.code = IMSG_SCRIPT_GO;
2169147072Sbrooks	hdr.len = sizeof(struct imsg_hdr);
2170147072Sbrooks
2171147072Sbrooks	if ((buf = buf_open(hdr.len)) == NULL)
2172147072Sbrooks		error("buf_open: %m");
2173147072Sbrooks
2174147072Sbrooks	if (buf_add(buf, &hdr, sizeof(hdr)))
2175147072Sbrooks		error("buf_add: %m");
2176147072Sbrooks
2177147072Sbrooks	if (buf_close(privfd, buf) == -1)
2178147072Sbrooks		error("buf_close: %m");
2179147072Sbrooks
2180147072Sbrooks	bzero(&hdr, sizeof(hdr));
2181147072Sbrooks	buf_read(privfd, &hdr, sizeof(hdr));
2182147072Sbrooks	if (hdr.code != IMSG_SCRIPT_GO_RET)
2183147072Sbrooks		error("unexpected msg type %u", hdr.code);
2184147072Sbrooks	if (hdr.len != sizeof(hdr) + sizeof(int))
2185147072Sbrooks		error("received corrupted message");
2186147072Sbrooks	buf_read(privfd, &ret, sizeof(ret));
2187147072Sbrooks
2188209756Sbrian	scripttime = time(NULL);
2189209756Sbrian
2190147072Sbrooks	return (ret);
2191147072Sbrooks}
2192147072Sbrooks
2193147072Sbrooksint
2194147072Sbrookspriv_script_go(void)
2195147072Sbrooks{
2196147072Sbrooks	char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI";
2197147072Sbrooks	static char client_path[] = CLIENT_PATH;
2198147072Sbrooks	struct interface_info *ip = ifi;
2199147072Sbrooks	int pid, wpid, wstatus;
2200147072Sbrooks
2201147072Sbrooks	scripttime = time(NULL);
2202147072Sbrooks
2203147072Sbrooks	if (ip) {
2204147072Sbrooks		scriptName = ip->client->config->script_name;
2205147072Sbrooks		envp = ip->client->scriptEnv;
2206147072Sbrooks	} else {
2207147072Sbrooks		scriptName = top_level_config.script_name;
2208147072Sbrooks		epp[0] = reason;
2209147072Sbrooks		epp[1] = client_path;
2210147072Sbrooks		epp[2] = NULL;
2211147072Sbrooks		envp = epp;
2212147072Sbrooks	}
2213147072Sbrooks
2214147072Sbrooks	argv[0] = scriptName;
2215147072Sbrooks	argv[1] = NULL;
2216147072Sbrooks
2217147072Sbrooks	pid = fork();
2218147072Sbrooks	if (pid < 0) {
2219147072Sbrooks		error("fork: %m");
2220147072Sbrooks		wstatus = 0;
2221147072Sbrooks	} else if (pid) {
2222147072Sbrooks		do {
2223147072Sbrooks			wpid = wait(&wstatus);
2224147072Sbrooks		} while (wpid != pid && wpid > 0);
2225147072Sbrooks		if (wpid < 0) {
2226147072Sbrooks			error("wait: %m");
2227147072Sbrooks			wstatus = 0;
2228147072Sbrooks		}
2229147072Sbrooks	} else {
2230147072Sbrooks		execve(scriptName, argv, envp);
2231147072Sbrooks		error("execve (%s, ...): %m", scriptName);
2232147072Sbrooks	}
2233147072Sbrooks
2234147072Sbrooks	if (ip)
2235147072Sbrooks		script_flush_env(ip->client);
2236147072Sbrooks
2237147072Sbrooks	return (wstatus & 0xff);
2238147072Sbrooks}
2239147072Sbrooks
2240147072Sbrooksvoid
2241147072Sbrooksscript_set_env(struct client_state *client, const char *prefix,
2242147072Sbrooks    const char *name, const char *value)
2243147072Sbrooks{
2244147072Sbrooks	int i, j, namelen;
2245147072Sbrooks
2246147072Sbrooks	namelen = strlen(name);
2247147072Sbrooks
2248147072Sbrooks	for (i = 0; client->scriptEnv[i]; i++)
2249147072Sbrooks		if (strncmp(client->scriptEnv[i], name, namelen) == 0 &&
2250147072Sbrooks		    client->scriptEnv[i][namelen] == '=')
2251147072Sbrooks			break;
2252147072Sbrooks
2253147072Sbrooks	if (client->scriptEnv[i])
2254147072Sbrooks		/* Reuse the slot. */
2255147072Sbrooks		free(client->scriptEnv[i]);
2256147072Sbrooks	else {
2257147072Sbrooks		/* New variable.  Expand if necessary. */
2258147072Sbrooks		if (i >= client->scriptEnvsize - 1) {
2259147072Sbrooks			char **newscriptEnv;
2260147072Sbrooks			int newscriptEnvsize = client->scriptEnvsize + 50;
2261147072Sbrooks
2262147072Sbrooks			newscriptEnv = realloc(client->scriptEnv,
2263147072Sbrooks			    newscriptEnvsize);
2264147072Sbrooks			if (newscriptEnv == NULL) {
2265147072Sbrooks				free(client->scriptEnv);
2266147072Sbrooks				client->scriptEnv = NULL;
2267147072Sbrooks				client->scriptEnvsize = 0;
2268147072Sbrooks				error("script_set_env: no memory for variable");
2269147072Sbrooks			}
2270147072Sbrooks			client->scriptEnv = newscriptEnv;
2271147072Sbrooks			client->scriptEnvsize = newscriptEnvsize;
2272147072Sbrooks		}
2273147072Sbrooks		/* need to set the NULL pointer at end of array beyond
2274147072Sbrooks		   the new slot. */
2275147072Sbrooks		client->scriptEnv[i + 1] = NULL;
2276147072Sbrooks	}
2277147072Sbrooks	/* Allocate space and format the variable in the appropriate slot. */
2278147072Sbrooks	client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 +
2279147072Sbrooks	    strlen(value) + 1);
2280147072Sbrooks	if (client->scriptEnv[i] == NULL)
2281147072Sbrooks		error("script_set_env: no memory for variable assignment");
2282147072Sbrooks
2283147072Sbrooks	/* No `` or $() command substitution allowed in environment values! */
2284147072Sbrooks	for (j=0; j < strlen(value); j++)
2285147072Sbrooks		switch (value[j]) {
2286147072Sbrooks		case '`':
2287147072Sbrooks		case '$':
2288147072Sbrooks			error("illegal character (%c) in value '%s'", value[j],
2289147072Sbrooks			    value);
2290147072Sbrooks			/* not reached */
2291147072Sbrooks		}
2292147072Sbrooks	snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) +
2293147072Sbrooks	    1 + strlen(value) + 1, "%s%s=%s", prefix, name, value);
2294147072Sbrooks}
2295147072Sbrooks
2296147072Sbrooksvoid
2297147072Sbrooksscript_flush_env(struct client_state *client)
2298147072Sbrooks{
2299147072Sbrooks	int i;
2300147072Sbrooks
2301147072Sbrooks	for (i = 0; client->scriptEnv[i]; i++) {
2302147072Sbrooks		free(client->scriptEnv[i]);
2303147072Sbrooks		client->scriptEnv[i] = NULL;
2304147072Sbrooks	}
2305147072Sbrooks	client->scriptEnvsize = 0;
2306147072Sbrooks}
2307147072Sbrooks
2308147072Sbrooksint
2309147072Sbrooksdhcp_option_ev_name(char *buf, size_t buflen, struct option *option)
2310147072Sbrooks{
2311147072Sbrooks	int i;
2312147072Sbrooks
2313147072Sbrooks	for (i = 0; option->name[i]; i++) {
2314147072Sbrooks		if (i + 1 == buflen)
2315147072Sbrooks			return 0;
2316147072Sbrooks		if (option->name[i] == '-')
2317147072Sbrooks			buf[i] = '_';
2318147072Sbrooks		else
2319147072Sbrooks			buf[i] = option->name[i];
2320147072Sbrooks	}
2321147072Sbrooks
2322147072Sbrooks	buf[i] = 0;
2323147072Sbrooks	return 1;
2324147072Sbrooks}
2325147072Sbrooks
2326147072Sbrooksvoid
2327147072Sbrooksgo_daemon(void)
2328147072Sbrooks{
2329147072Sbrooks	static int state = 0;
2330147072Sbrooks
2331147072Sbrooks	if (no_daemon || state)
2332147072Sbrooks		return;
2333147072Sbrooks
2334147072Sbrooks	state = 1;
2335147072Sbrooks
2336147072Sbrooks	/* Stop logging to stderr... */
2337147072Sbrooks	log_perror = 0;
2338147072Sbrooks
2339147072Sbrooks	if (daemon(1, 0) == -1)
2340147072Sbrooks		error("daemon");
2341147072Sbrooks
2342231277Sbapt	if (pidfile != NULL)
2343231277Sbapt		pidfile_write(pidfile);
2344231277Sbapt
2345147072Sbrooks	/* we are chrooted, daemon(3) fails to open /dev/null */
2346147072Sbrooks	if (nullfd != -1) {
2347147072Sbrooks		dup2(nullfd, STDIN_FILENO);
2348147072Sbrooks		dup2(nullfd, STDOUT_FILENO);
2349147072Sbrooks		dup2(nullfd, STDERR_FILENO);
2350147072Sbrooks		close(nullfd);
2351147072Sbrooks		nullfd = -1;
2352147072Sbrooks	}
2353147072Sbrooks}
2354147072Sbrooks
2355147072Sbrooksint
2356147072Sbrookscheck_option(struct client_lease *l, int option)
2357147072Sbrooks{
2358147072Sbrooks	char *opbuf;
2359147072Sbrooks	char *sbuf;
2360147072Sbrooks
2361147072Sbrooks	/* we use this, since this is what gets passed to dhclient-script */
2362147072Sbrooks
2363147072Sbrooks	opbuf = pretty_print_option(option, l->options[option].data,
2364147072Sbrooks	    l->options[option].len, 0, 0);
2365147072Sbrooks
2366147072Sbrooks	sbuf = option_as_string(option, l->options[option].data,
2367147072Sbrooks	    l->options[option].len);
2368147072Sbrooks
2369147072Sbrooks	switch (option) {
2370147072Sbrooks	case DHO_SUBNET_MASK:
2371147072Sbrooks	case DHO_TIME_SERVERS:
2372147072Sbrooks	case DHO_NAME_SERVERS:
2373147072Sbrooks	case DHO_ROUTERS:
2374147072Sbrooks	case DHO_DOMAIN_NAME_SERVERS:
2375147072Sbrooks	case DHO_LOG_SERVERS:
2376147072Sbrooks	case DHO_COOKIE_SERVERS:
2377147072Sbrooks	case DHO_LPR_SERVERS:
2378147072Sbrooks	case DHO_IMPRESS_SERVERS:
2379147072Sbrooks	case DHO_RESOURCE_LOCATION_SERVERS:
2380147072Sbrooks	case DHO_SWAP_SERVER:
2381147072Sbrooks	case DHO_BROADCAST_ADDRESS:
2382147072Sbrooks	case DHO_NIS_SERVERS:
2383147072Sbrooks	case DHO_NTP_SERVERS:
2384147072Sbrooks	case DHO_NETBIOS_NAME_SERVERS:
2385147072Sbrooks	case DHO_NETBIOS_DD_SERVER:
2386147072Sbrooks	case DHO_FONT_SERVERS:
2387147072Sbrooks	case DHO_DHCP_SERVER_IDENTIFIER:
2388183974Sbrooks	case DHO_NISPLUS_SERVERS:
2389183974Sbrooks	case DHO_MOBILE_IP_HOME_AGENT:
2390147689Sbrooks	case DHO_SMTP_SERVER:
2391147689Sbrooks	case DHO_POP_SERVER:
2392147689Sbrooks	case DHO_NNTP_SERVER:
2393147689Sbrooks	case DHO_WWW_SERVER:
2394147689Sbrooks	case DHO_FINGER_SERVER:
2395147689Sbrooks	case DHO_IRC_SERVER:
2396183974Sbrooks	case DHO_STREETTALK_SERVER:
2397183974Sbrooks	case DHO_STREETTALK_DA_SERVER:
2398147072Sbrooks		if (!ipv4addrs(opbuf)) {
2399147072Sbrooks			warning("Invalid IP address in option: %s", opbuf);
2400147072Sbrooks			return (0);
2401147072Sbrooks		}
2402147072Sbrooks		return (1)  ;
2403147072Sbrooks	case DHO_HOST_NAME:
2404147072Sbrooks	case DHO_NIS_DOMAIN:
2405183974Sbrooks	case DHO_NISPLUS_DOMAIN:
2406183974Sbrooks	case DHO_TFTP_SERVER_NAME:
2407147072Sbrooks		if (!res_hnok(sbuf)) {
2408147072Sbrooks			warning("Bogus Host Name option %d: %s (%s)", option,
2409147072Sbrooks			    sbuf, opbuf);
2410153287Sbrooks			l->options[option].len = 0;
2411153287Sbrooks			free(l->options[option].data);
2412147072Sbrooks		}
2413147072Sbrooks		return (1);
2414147686Sbrooks	case DHO_DOMAIN_NAME:
2415230597Sdumbbell	case DHO_DOMAIN_SEARCH:
2416149639Sbrooks		if (!res_hnok(sbuf)) {
2417149639Sbrooks			if (!check_search(sbuf)) {
2418149639Sbrooks				warning("Bogus domain search list %d: %s (%s)",
2419149639Sbrooks				    option, sbuf, opbuf);
2420153287Sbrooks				l->options[option].len = 0;
2421153287Sbrooks				free(l->options[option].data);
2422149639Sbrooks			}
2423149639Sbrooks		}
2424149639Sbrooks		return (1);
2425147072Sbrooks	case DHO_PAD:
2426147072Sbrooks	case DHO_TIME_OFFSET:
2427147072Sbrooks	case DHO_BOOT_SIZE:
2428147072Sbrooks	case DHO_MERIT_DUMP:
2429147072Sbrooks	case DHO_ROOT_PATH:
2430147072Sbrooks	case DHO_EXTENSIONS_PATH:
2431147072Sbrooks	case DHO_IP_FORWARDING:
2432147072Sbrooks	case DHO_NON_LOCAL_SOURCE_ROUTING:
2433147072Sbrooks	case DHO_POLICY_FILTER:
2434147072Sbrooks	case DHO_MAX_DGRAM_REASSEMBLY:
2435147072Sbrooks	case DHO_DEFAULT_IP_TTL:
2436147072Sbrooks	case DHO_PATH_MTU_AGING_TIMEOUT:
2437147072Sbrooks	case DHO_PATH_MTU_PLATEAU_TABLE:
2438147072Sbrooks	case DHO_INTERFACE_MTU:
2439147072Sbrooks	case DHO_ALL_SUBNETS_LOCAL:
2440147072Sbrooks	case DHO_PERFORM_MASK_DISCOVERY:
2441147072Sbrooks	case DHO_MASK_SUPPLIER:
2442147072Sbrooks	case DHO_ROUTER_DISCOVERY:
2443147072Sbrooks	case DHO_ROUTER_SOLICITATION_ADDRESS:
2444147072Sbrooks	case DHO_STATIC_ROUTES:
2445147072Sbrooks	case DHO_TRAILER_ENCAPSULATION:
2446147072Sbrooks	case DHO_ARP_CACHE_TIMEOUT:
2447147072Sbrooks	case DHO_IEEE802_3_ENCAPSULATION:
2448147072Sbrooks	case DHO_DEFAULT_TCP_TTL:
2449147072Sbrooks	case DHO_TCP_KEEPALIVE_INTERVAL:
2450147072Sbrooks	case DHO_TCP_KEEPALIVE_GARBAGE:
2451147072Sbrooks	case DHO_VENDOR_ENCAPSULATED_OPTIONS:
2452147072Sbrooks	case DHO_NETBIOS_NODE_TYPE:
2453147072Sbrooks	case DHO_NETBIOS_SCOPE:
2454147072Sbrooks	case DHO_X_DISPLAY_MANAGER:
2455147072Sbrooks	case DHO_DHCP_REQUESTED_ADDRESS:
2456147072Sbrooks	case DHO_DHCP_LEASE_TIME:
2457147072Sbrooks	case DHO_DHCP_OPTION_OVERLOAD:
2458147072Sbrooks	case DHO_DHCP_MESSAGE_TYPE:
2459147072Sbrooks	case DHO_DHCP_PARAMETER_REQUEST_LIST:
2460147072Sbrooks	case DHO_DHCP_MESSAGE:
2461147072Sbrooks	case DHO_DHCP_MAX_MESSAGE_SIZE:
2462147072Sbrooks	case DHO_DHCP_RENEWAL_TIME:
2463147072Sbrooks	case DHO_DHCP_REBINDING_TIME:
2464147072Sbrooks	case DHO_DHCP_CLASS_IDENTIFIER:
2465147072Sbrooks	case DHO_DHCP_CLIENT_IDENTIFIER:
2466183974Sbrooks	case DHO_BOOTFILE_NAME:
2467147072Sbrooks	case DHO_DHCP_USER_CLASS_ID:
2468147072Sbrooks	case DHO_END:
2469147072Sbrooks		return (1);
2470166602Semaste	case DHO_CLASSLESS_ROUTES:
2471166602Semaste		return (check_classless_option(l->options[option].data,
2472166602Semaste		    l->options[option].len));
2473147072Sbrooks	default:
2474147072Sbrooks		warning("unknown dhcp option value 0x%x", option);
2475147072Sbrooks		return (unknown_ok);
2476147072Sbrooks	}
2477147072Sbrooks}
2478147072Sbrooks
2479166602Semaste/* RFC 3442 The Classless Static Routes option checks */
2480147072Sbrooksint
2481166602Semastecheck_classless_option(unsigned char *data, int len)
2482166602Semaste{
2483166602Semaste	int i = 0;
2484166602Semaste	unsigned char width;
2485166602Semaste	in_addr_t addr, mask;
2486166602Semaste
2487166602Semaste	if (len < 5) {
2488166602Semaste		warning("Too small length: %d", len);
2489166602Semaste		return (0);
2490166602Semaste	}
2491166602Semaste	while(i < len) {
2492166602Semaste		width = data[i++];
2493166602Semaste		if (width == 0) {
2494166602Semaste			i += 4;
2495166602Semaste			continue;
2496166602Semaste		} else if (width < 9) {
2497166602Semaste			addr =  (in_addr_t)(data[i] 	<< 24);
2498166602Semaste			i += 1;
2499166602Semaste		} else if (width < 17) {
2500166602Semaste			addr =  (in_addr_t)(data[i] 	<< 24) +
2501166602Semaste				(in_addr_t)(data[i + 1]	<< 16);
2502166602Semaste			i += 2;
2503166602Semaste		} else if (width < 25) {
2504166602Semaste			addr =  (in_addr_t)(data[i] 	<< 24) +
2505166602Semaste				(in_addr_t)(data[i + 1]	<< 16) +
2506166602Semaste				(in_addr_t)(data[i + 2]	<< 8);
2507166602Semaste			i += 3;
2508166602Semaste		} else if (width < 33) {
2509166602Semaste			addr =  (in_addr_t)(data[i] 	<< 24) +
2510166602Semaste				(in_addr_t)(data[i + 1]	<< 16) +
2511166602Semaste				(in_addr_t)(data[i + 2]	<< 8)  +
2512166602Semaste				data[i + 3];
2513166602Semaste			i += 4;
2514166602Semaste		} else {
2515166602Semaste			warning("Incorrect subnet width: %d", width);
2516166602Semaste			return (0);
2517166602Semaste		}
2518166602Semaste		mask = (in_addr_t)(~0) << (32 - width);
2519166602Semaste		addr = ntohl(addr);
2520166602Semaste		mask = ntohl(mask);
2521166602Semaste
2522166602Semaste		/*
2523166602Semaste		 * From RFC 3442:
2524166602Semaste		 * ... After deriving a subnet number and subnet mask
2525166602Semaste		 * from each destination descriptor, the DHCP client
2526166602Semaste		 * MUST zero any bits in the subnet number where the
2527166602Semaste		 * corresponding bit in the mask is zero...
2528166602Semaste		 */
2529166602Semaste		if ((addr & mask) != addr) {
2530166602Semaste			addr &= mask;
2531166602Semaste			data[i - 1] = (unsigned char)(
2532166602Semaste				(addr >> (((32 - width)/8)*8)) & 0xFF);
2533166602Semaste		}
2534166602Semaste		i += 4;
2535166602Semaste	}
2536166602Semaste	if (i > len) {
2537166602Semaste		warning("Incorrect data length: %d (must be %d)", len, i);
2538166602Semaste		return (0);
2539166602Semaste	}
2540166602Semaste	return (1);
2541166602Semaste}
2542166602Semaste
2543166602Semasteint
2544147072Sbrooksres_hnok(const char *dn)
2545147072Sbrooks{
2546147072Sbrooks	int pch = PERIOD, ch = *dn++;
2547147072Sbrooks
2548147072Sbrooks	while (ch != '\0') {
2549147072Sbrooks		int nch = *dn++;
2550147072Sbrooks
2551147072Sbrooks		if (periodchar(ch)) {
2552147072Sbrooks			;
2553147072Sbrooks		} else if (periodchar(pch)) {
2554147072Sbrooks			if (!borderchar(ch))
2555147072Sbrooks				return (0);
2556147072Sbrooks		} else if (periodchar(nch) || nch == '\0') {
2557147072Sbrooks			if (!borderchar(ch))
2558147072Sbrooks				return (0);
2559147072Sbrooks		} else {
2560147072Sbrooks			if (!middlechar(ch))
2561147072Sbrooks				return (0);
2562147072Sbrooks		}
2563147072Sbrooks		pch = ch, ch = nch;
2564147072Sbrooks	}
2565147072Sbrooks	return (1);
2566147072Sbrooks}
2567147072Sbrooks
2568149639Sbrooksint
2569149639Sbrookscheck_search(const char *srch)
2570149639Sbrooks{
2571149639Sbrooks        int pch = PERIOD, ch = *srch++;
2572149639Sbrooks	int domains = 1;
2573149639Sbrooks
2574149639Sbrooks	/* 256 char limit re resolv.conf(5) */
2575149639Sbrooks	if (strlen(srch) > 256)
2576149639Sbrooks		return (0);
2577149639Sbrooks
2578149639Sbrooks	while (whitechar(ch))
2579149639Sbrooks		ch = *srch++;
2580149639Sbrooks
2581149639Sbrooks        while (ch != '\0') {
2582149639Sbrooks                int nch = *srch++;
2583149639Sbrooks
2584149639Sbrooks                if (periodchar(ch) || whitechar(ch)) {
2585149639Sbrooks                        ;
2586149639Sbrooks                } else if (periodchar(pch)) {
2587149639Sbrooks                        if (!borderchar(ch))
2588149639Sbrooks                                return (0);
2589149639Sbrooks                } else if (periodchar(nch) || nch == '\0') {
2590149639Sbrooks                        if (!borderchar(ch))
2591149639Sbrooks                                return (0);
2592149639Sbrooks                } else {
2593149639Sbrooks                        if (!middlechar(ch))
2594149639Sbrooks                                return (0);
2595149639Sbrooks                }
2596149639Sbrooks		if (!whitechar(ch)) {
2597149639Sbrooks			pch = ch;
2598149639Sbrooks		} else {
2599149639Sbrooks			while (whitechar(nch)) {
2600149639Sbrooks				nch = *srch++;
2601149639Sbrooks			}
2602149639Sbrooks			if (nch != '\0')
2603149639Sbrooks				domains++;
2604149639Sbrooks			pch = PERIOD;
2605149639Sbrooks		}
2606149639Sbrooks		ch = nch;
2607149639Sbrooks        }
2608149639Sbrooks	/* 6 domain limit re resolv.conf(5) */
2609149639Sbrooks	if (domains > 6)
2610149639Sbrooks		return (0);
2611149639Sbrooks        return (1);
2612149639Sbrooks}
2613149639Sbrooks
2614147072Sbrooks/* Does buf consist only of dotted decimal ipv4 addrs?
2615147072Sbrooks * return how many if so,
2616147072Sbrooks * otherwise, return 0
2617147072Sbrooks */
2618147072Sbrooksint
2619147072Sbrooksipv4addrs(char * buf)
2620147072Sbrooks{
2621147072Sbrooks	struct in_addr jnk;
2622147072Sbrooks	int count = 0;
2623147072Sbrooks
2624147072Sbrooks	while (inet_aton(buf, &jnk) == 1){
2625147072Sbrooks		count++;
2626147072Sbrooks		while (periodchar(*buf) || digitchar(*buf))
2627147072Sbrooks			buf++;
2628147072Sbrooks		if (*buf == '\0')
2629147072Sbrooks			return (count);
2630147072Sbrooks		while (*buf ==  ' ')
2631147072Sbrooks			buf++;
2632147072Sbrooks	}
2633147072Sbrooks	return (0);
2634147072Sbrooks}
2635147072Sbrooks
2636147072Sbrooks
2637147072Sbrookschar *
2638147072Sbrooksoption_as_string(unsigned int code, unsigned char *data, int len)
2639147072Sbrooks{
2640147072Sbrooks	static char optbuf[32768]; /* XXX */
2641147072Sbrooks	char *op = optbuf;
2642147072Sbrooks	int opleft = sizeof(optbuf);
2643147072Sbrooks	unsigned char *dp = data;
2644147072Sbrooks
2645147072Sbrooks	if (code > 255)
2646147072Sbrooks		error("option_as_string: bad code %d", code);
2647147072Sbrooks
2648147072Sbrooks	for (; dp < data + len; dp++) {
2649147072Sbrooks		if (!isascii(*dp) || !isprint(*dp)) {
2650147072Sbrooks			if (dp + 1 != data + len || *dp != 0) {
2651147072Sbrooks				snprintf(op, opleft, "\\%03o", *dp);
2652147072Sbrooks				op += 4;
2653147072Sbrooks				opleft -= 4;
2654147072Sbrooks			}
2655147072Sbrooks		} else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
2656147072Sbrooks		    *dp == '`' || *dp == '\\') {
2657147072Sbrooks			*op++ = '\\';
2658147072Sbrooks			*op++ = *dp;
2659147072Sbrooks			opleft -= 2;
2660147072Sbrooks		} else {
2661147072Sbrooks			*op++ = *dp;
2662147072Sbrooks			opleft--;
2663147072Sbrooks		}
2664147072Sbrooks	}
2665147072Sbrooks	if (opleft < 1)
2666147072Sbrooks		goto toobig;
2667147072Sbrooks	*op = 0;
2668147072Sbrooks	return optbuf;
2669147072Sbrookstoobig:
2670147072Sbrooks	warning("dhcp option too large");
2671147072Sbrooks	return "<error>";
2672147072Sbrooks}
2673147072Sbrooks
2674147072Sbrooksint
2675147072Sbrooksfork_privchld(int fd, int fd2)
2676147072Sbrooks{
2677147072Sbrooks	struct pollfd pfd[1];
2678147072Sbrooks	int nfds;
2679147072Sbrooks
2680147072Sbrooks	switch (fork()) {
2681147072Sbrooks	case -1:
2682147072Sbrooks		error("cannot fork");
2683147072Sbrooks	case 0:
2684147072Sbrooks		break;
2685147072Sbrooks	default:
2686147072Sbrooks		return (0);
2687147072Sbrooks	}
2688147072Sbrooks
2689147072Sbrooks	setproctitle("%s [priv]", ifi->name);
2690147072Sbrooks
2691180130Sed	setsid();
2692147072Sbrooks	dup2(nullfd, STDIN_FILENO);
2693147072Sbrooks	dup2(nullfd, STDOUT_FILENO);
2694147072Sbrooks	dup2(nullfd, STDERR_FILENO);
2695147072Sbrooks	close(nullfd);
2696147072Sbrooks	close(fd2);
2697147072Sbrooks
2698147072Sbrooks	for (;;) {
2699147072Sbrooks		pfd[0].fd = fd;
2700147072Sbrooks		pfd[0].events = POLLIN;
2701147072Sbrooks		if ((nfds = poll(pfd, 1, INFTIM)) == -1)
2702147072Sbrooks			if (errno != EINTR)
2703147072Sbrooks				error("poll error");
2704147072Sbrooks
2705147072Sbrooks		if (nfds == 0 || !(pfd[0].revents & POLLIN))
2706147072Sbrooks			continue;
2707147072Sbrooks
2708147072Sbrooks		dispatch_imsg(fd);
2709147072Sbrooks	}
2710147072Sbrooks}
2711