1 /* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */
4 * Copyright 2004 Henning Brauer <henning@openbsd.org>
5 * Copyright (c) 1995, 1996, 1997, 1998, 1999
6 * The Internet Software Consortium. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of The Internet Software Consortium nor the names
18 * of its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
22 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * This software has been written for the Internet Software Consortium
36 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
37 * Enterprises. To learn more about the Internet Software Consortium,
38 * see ``http://www.vix.com/isc''. To learn more about Vixie
39 * Enterprises, see ``http://www.vix.com''.
41 * This client was substantially modified and enhanced by Elliot Poger
42 * for use on Linux while he was working on the MosquitoNet project at
45 * The current version owes much to Elliot's Linux enhancements, but
46 * was substantially reorganized and partially rewritten by Ted Lemon
47 * so as to use the same networking framework that the Internet Software
48 * Consortium DHCP server uses. Much system-specific configuration code
49 * was moved into a shell script so that as support for more operating
50 * systems is added, it will not be necessary to port and maintain
51 * system-specific configuration code to these operating systems - instead,
52 * the shell script can invoke the native tools to accomplish the same
56 #include <sys/cdefs.h>
57 __FBSDID("$FreeBSD$");
62 #include <net80211/ieee80211_freebsd.h>
64 #ifndef _PATH_VAREMPTY
65 #define _PATH_VAREMPTY "/var/empty"
69 #define hyphenchar(c) ((c) == 0x2d)
70 #define bslashchar(c) ((c) == 0x5c)
71 #define periodchar(c) ((c) == PERIOD)
72 #define asterchar(c) ((c) == 0x2a)
73 #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
74 ((c) >= 0x61 && (c) <= 0x7a))
75 #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
76 #define whitechar(c) ((c) == ' ' || (c) == '\t')
78 #define borderchar(c) (alphachar(c) || digitchar(c))
79 #define middlechar(c) (borderchar(c) || hyphenchar(c))
80 #define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
82 #define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
85 time_t default_lease_time = 43200; /* 12 hours... */
87 char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
88 char *path_dhclient_db = NULL;
94 struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
95 struct in_addr inaddr_any;
96 struct sockaddr_in sockaddr_broadcast;
99 * ASSERT_STATE() does nothing now; it used to be
100 * assert (state_is == state_shouldbe).
102 #define ASSERT_STATE(state_is, state_shouldbe) {}
104 #define TIME_MAX 2147483647
111 struct interface_info *ifi;
113 int findproto(char *, int);
114 struct sockaddr *get_ifa(char *, int);
115 void routehandler(struct protocol *);
117 int check_option(struct client_lease *l, int option);
118 int ipv4addrs(char * buf);
119 int res_hnok(const char *dn);
120 int check_search(const char *srch);
121 char *option_as_string(unsigned int code, unsigned char *data, int len);
122 int fork_privchld(int, int);
125 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
126 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
131 findproto(char *cp, int n)
138 for (i = 1; i; i <<= 1) {
140 sa = (struct sockaddr *)cp;
146 if (sa->sa_family == AF_INET)
148 if (sa->sa_family == AF_INET6)
161 get_ifa(char *cp, int n)
168 for (i = 1; i; i <<= 1)
170 sa = (struct sockaddr *)cp;
178 struct iaddr defaddr = { 4 };
182 routehandler(struct protocol *p)
185 struct rt_msghdr *rtm;
186 struct if_msghdr *ifm;
187 struct ifa_msghdr *ifam;
188 struct if_announcemsghdr *ifan;
189 struct client_lease *l;
190 time_t t = time(NULL);
195 n = read(routefd, &msg, sizeof(msg));
196 rtm = (struct rt_msghdr *)msg;
197 if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen ||
198 rtm->rtm_version != RTM_VERSION)
201 switch (rtm->rtm_type) {
204 * XXX: If someone other than us adds our address,
205 * we should assume they are taking over from us,
206 * delete the lease record, and exit without modifying
211 ifam = (struct ifa_msghdr *)rtm;
213 if (ifam->ifam_index != ifi->index)
215 if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET)
217 if (scripttime == 0 || t < scripttime + 10)
220 sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs);
224 if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf))
225 error("king bula sez: len mismatch");
226 memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len);
227 if (addr_eq(a, defaddr))
230 for (l = ifi->client->active; l != NULL; l = l->next)
231 if (addr_eq(a, l->address))
234 if (l == NULL) /* deleted addr is not the one we set */
238 ifm = (struct if_msghdr *)rtm;
239 if (ifm->ifm_index != ifi->index)
241 if ((rtm->rtm_flags & RTF_UP) == 0)
245 ifan = (struct if_announcemsghdr *)rtm;
246 if (ifan->ifan_what == IFAN_DEPARTURE &&
247 ifan->ifan_index == ifi->index)
251 ifan = (struct if_announcemsghdr *)rtm;
252 if (ifan->ifan_index != ifi->index)
254 switch (ifan->ifan_what) {
255 case RTM_IEEE80211_ASSOC:
256 case RTM_IEEE80211_REASSOC:
259 case RTM_IEEE80211_DISASSOC:
261 * Clear existing state; transition to the init
262 * state and then wait for either a link down
263 * notification or an associate event.
265 if (ifi->client->active != NULL) {
266 script_init("EXPIRE", NULL);
267 script_write_params("old_",
268 ifi->client->active);
269 if (ifi->client->alias)
270 script_write_params("alias_",
274 ifi->client->state = S_INIT;
284 script_init("FAIL", NULL);
285 if (ifi->client->alias)
286 script_write_params("alias_", ifi->client->alias);
292 main(int argc, char *argv[])
294 extern char *__progname;
295 int ch, fd, quiet = 0, i = 0;
297 int immediate_daemon = 0;
300 /* Initially, log errors to stderr as well as to syslogd. */
301 openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
302 setlogmask(LOG_UPTO(LOG_INFO));
304 while ((ch = getopt(argc, argv, "bc:dl:qu")) != -1)
307 immediate_daemon = 1;
310 path_dhclient_conf = optarg;
316 path_dhclient_db = optarg;
334 if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL)
336 if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ)
337 error("Interface name too long");
338 if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s",
339 _PATH_DHCLIENT_DB, ifi->name) == -1)
348 memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
349 sockaddr_broadcast.sin_family = AF_INET;
350 sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
351 sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
352 sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast);
353 inaddr_any.s_addr = INADDR_ANY;
357 if (!interface_link_status(ifi->name)) {
358 fprintf(stderr, "%s: no link ...", ifi->name);
361 while (!interface_link_status(ifi->name)) {
362 fprintf(stderr, ".");
365 fprintf(stderr, " giving up\n");
370 fprintf(stderr, " got link\n");
373 if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
374 error("cannot open %s: %m", _PATH_DEVNULL);
376 if ((pw = getpwnam("_dhcp")) == NULL) {
377 warning("no such user: _dhcp, falling back to \"nobody\"");
378 if ((pw = getpwnam("nobody")) == NULL)
379 error("no such user: nobody");
382 if (pipe(pipe_fd) == -1)
385 fork_privchld(pipe_fd[0], pipe_fd[1]);
390 if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
391 error("can't open and lock %s: %m", path_dhclient_db);
392 read_client_leases();
393 rewrite_client_leases();
396 priv_script_init("PREINIT", NULL);
397 if (ifi->client->alias)
398 priv_script_write_params("alias_", ifi->client->alias);
401 if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
402 add_protocol("AF_ROUTE", routefd, routehandler, ifi);
404 /* set up the interface */
405 discover_interfaces(ifi);
407 if (chroot(_PATH_VAREMPTY) == -1)
409 if (chdir("/") == -1)
410 error("chdir(\"/\")");
412 if (setgroups(1, &pw->pw_gid) ||
413 setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
414 seteuid(pw->pw_uid) || setuid(pw->pw_uid))
415 error("can't drop privileges: %m");
419 setproctitle("%s", ifi->name);
421 if (immediate_daemon)
424 ifi->client->state = S_INIT;
427 bootp_packet_handler = do_packet;
438 extern char *__progname;
440 fprintf(stderr, "usage: %s [-bdqu] ", __progname);
441 fprintf(stderr, "[-c conffile] [-l leasefile] interface\n");
448 * Each routine is called from the dhclient_state_machine() in one of
450 * -> entering INIT state
451 * -> recvpacket_flag == 0: timeout in this state
452 * -> otherwise: received a packet in this state
454 * Return conditions as handled by dhclient_state_machine():
455 * Returns 1, sendpacket_flag = 1: send packet, reset timer.
456 * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
457 * Returns 0: finish the nap which was interrupted for no good reason.
459 * Several per-interface variables are used to keep track of the process:
460 * active_lease: the lease that is being used on the interface
461 * (null pointer if not configured yet).
462 * offered_leases: leases corresponding to DHCPOFFER messages that have
463 * been sent to us by DHCP servers.
464 * acked_leases: leases corresponding to DHCPACK messages that have been
465 * sent to us by DHCP servers.
466 * sendpacket: DHCP packet we're trying to send.
467 * destination: IP address to send sendpacket to
468 * In addition, there are several relevant per-lease variables.
469 * T1_expiry, T2_expiry, lease_expiry: lease milestones
470 * In the active lease, these control the process of renewing the lease;
471 * In leases on the acked_leases list, this simply determines when we
472 * can no longer legitimately use the lease.
476 state_reboot(void *ipp)
478 struct interface_info *ip = ipp;
480 /* If we don't remember an active lease, go straight to INIT. */
481 if (!ip->client->active || ip->client->active->is_bootp) {
486 /* We are in the rebooting state. */
487 ip->client->state = S_REBOOTING;
489 /* make_request doesn't initialize xid because it normally comes
490 from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
491 so pick an xid now. */
492 ip->client->xid = arc4random();
494 /* Make a DHCPREQUEST packet, and set appropriate per-interface
496 make_request(ip, ip->client->active);
497 ip->client->destination = iaddr_broadcast;
498 ip->client->first_sending = cur_time;
499 ip->client->interval = ip->client->config->initial_interval;
501 /* Zap the medium list... */
502 ip->client->medium = NULL;
504 /* Send out the first DHCPREQUEST packet. */
509 * Called when a lease has completely expired and we've
510 * been unable to renew it.
513 state_init(void *ipp)
515 struct interface_info *ip = ipp;
517 ASSERT_STATE(state, S_INIT);
519 /* Make a DHCPDISCOVER packet, and set appropriate per-interface
521 make_discover(ip, ip->client->active);
522 ip->client->xid = ip->client->packet.xid;
523 ip->client->destination = iaddr_broadcast;
524 ip->client->state = S_SELECTING;
525 ip->client->first_sending = cur_time;
526 ip->client->interval = ip->client->config->initial_interval;
528 /* Add an immediate timeout to cause the first DHCPDISCOVER packet
534 * state_selecting is called when one or more DHCPOFFER packets
535 * have been received and a configurable period of time has passed.
538 state_selecting(void *ipp)
540 struct interface_info *ip = ipp;
541 struct client_lease *lp, *next, *picked;
543 ASSERT_STATE(state, S_SELECTING);
545 /* Cancel state_selecting and send_discover timeouts, since either
546 one could have got us here. */
547 cancel_timeout(state_selecting, ip);
548 cancel_timeout(send_discover, ip);
550 /* We have received one or more DHCPOFFER packets. Currently,
551 the only criterion by which we judge leases is whether or
552 not we get a response when we arp for them. */
554 for (lp = ip->client->offered_leases; lp; lp = next) {
557 /* Check to see if we got an ARPREPLY for the address
558 in this particular lease. */
560 script_init("ARPCHECK", lp->medium);
561 script_write_params("check_", lp);
563 /* If the ARPCHECK code detects another
564 machine using the offered address, it exits
565 nonzero. We need to send a DHCPDECLINE and
568 make_decline(ip, lp);
576 free_client_lease(lp);
579 ip->client->offered_leases = NULL;
581 /* If we just tossed all the leases we were offered, go back
584 ip->client->state = S_INIT;
589 /* If it was a BOOTREPLY, we can just take the address right now. */
590 if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
591 ip->client->new = picked;
593 /* Make up some lease expiry times
594 XXX these should be configurable. */
595 ip->client->new->expiry = cur_time + 12000;
596 ip->client->new->renewal += cur_time + 8000;
597 ip->client->new->rebind += cur_time + 10000;
599 ip->client->state = S_REQUESTING;
601 /* Bind to the address we received. */
606 /* Go to the REQUESTING state. */
607 ip->client->destination = iaddr_broadcast;
608 ip->client->state = S_REQUESTING;
609 ip->client->first_sending = cur_time;
610 ip->client->interval = ip->client->config->initial_interval;
612 /* Make a DHCPREQUEST packet from the lease we picked. */
613 make_request(ip, picked);
614 ip->client->xid = ip->client->packet.xid;
616 /* Toss the lease we picked - we'll get it back in a DHCPACK. */
617 free_client_lease(picked);
619 /* Add an immediate timeout to send the first DHCPREQUEST packet. */
623 /* state_requesting is called when we receive a DHCPACK message after
624 having sent out one or more DHCPREQUEST packets. */
627 dhcpack(struct packet *packet)
629 struct interface_info *ip = packet->interface;
630 struct client_lease *lease;
632 /* If we're not receptive to an offer right now, or if the offer
633 has an unrecognizable transaction id, then just drop it. */
634 if (packet->interface->client->xid != packet->raw->xid ||
635 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
636 (memcmp(packet->interface->hw_address.haddr,
637 packet->raw->chaddr, packet->raw->hlen)))
640 if (ip->client->state != S_REBOOTING &&
641 ip->client->state != S_REQUESTING &&
642 ip->client->state != S_RENEWING &&
643 ip->client->state != S_REBINDING)
646 note("DHCPACK from %s", piaddr(packet->client_addr));
648 lease = packet_to_lease(packet);
650 note("packet_to_lease failed.");
654 ip->client->new = lease;
656 /* Stop resending DHCPREQUEST. */
657 cancel_timeout(send_request, ip);
659 /* Figure out the lease time. */
660 if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
661 ip->client->new->expiry = getULong(
662 ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
664 ip->client->new->expiry = default_lease_time;
665 /* A number that looks negative here is really just very large,
666 because the lease expiry offset is unsigned. */
667 if (ip->client->new->expiry < 0)
668 ip->client->new->expiry = TIME_MAX;
669 /* XXX should be fixed by resetting the client state */
670 if (ip->client->new->expiry < 60)
671 ip->client->new->expiry = 60;
673 /* Take the server-provided renewal time if there is one;
674 otherwise figure it out according to the spec. */
675 if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
676 ip->client->new->renewal = getULong(
677 ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
679 ip->client->new->renewal = ip->client->new->expiry / 2;
681 /* Same deal with the rebind time. */
682 if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
683 ip->client->new->rebind = getULong(
684 ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
686 ip->client->new->rebind = ip->client->new->renewal +
687 ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
689 ip->client->new->expiry += cur_time;
690 /* Lease lengths can never be negative. */
691 if (ip->client->new->expiry < cur_time)
692 ip->client->new->expiry = TIME_MAX;
693 ip->client->new->renewal += cur_time;
694 if (ip->client->new->renewal < cur_time)
695 ip->client->new->renewal = TIME_MAX;
696 ip->client->new->rebind += cur_time;
697 if (ip->client->new->rebind < cur_time)
698 ip->client->new->rebind = TIME_MAX;
704 bind_lease(struct interface_info *ip)
706 /* Remember the medium. */
707 ip->client->new->medium = ip->client->medium;
709 /* Write out the new lease. */
710 write_client_lease(ip, ip->client->new, 0);
712 /* Run the client script with the new parameters. */
713 script_init((ip->client->state == S_REQUESTING ? "BOUND" :
714 (ip->client->state == S_RENEWING ? "RENEW" :
715 (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))),
716 ip->client->new->medium);
717 if (ip->client->active && ip->client->state != S_REBOOTING)
718 script_write_params("old_", ip->client->active);
719 script_write_params("new_", ip->client->new);
720 if (ip->client->alias)
721 script_write_params("alias_", ip->client->alias);
724 /* Replace the old active lease with the new one. */
725 if (ip->client->active)
726 free_client_lease(ip->client->active);
727 ip->client->active = ip->client->new;
728 ip->client->new = NULL;
730 /* Set up a timeout to start the renewal process. */
731 add_timeout(ip->client->active->renewal, state_bound, ip);
733 note("bound to %s -- renewal in %d seconds.",
734 piaddr(ip->client->active->address),
735 (int)(ip->client->active->renewal - cur_time));
736 ip->client->state = S_BOUND;
737 reinitialize_interfaces();
742 * state_bound is called when we've successfully bound to a particular
743 * lease, but the renewal time on that lease has expired. We are
744 * expected to unicast a DHCPREQUEST to the server that gave us our
748 state_bound(void *ipp)
750 struct interface_info *ip = ipp;
752 ASSERT_STATE(state, S_BOUND);
754 /* T1 has expired. */
755 make_request(ip, ip->client->active);
756 ip->client->xid = ip->client->packet.xid;
758 if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
759 memcpy(ip->client->destination.iabuf, ip->client->active->
760 options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
761 ip->client->destination.len = 4;
763 ip->client->destination = iaddr_broadcast;
765 ip->client->first_sending = cur_time;
766 ip->client->interval = ip->client->config->initial_interval;
767 ip->client->state = S_RENEWING;
769 /* Send the first packet immediately. */
774 bootp(struct packet *packet)
776 struct iaddrlist *ap;
778 if (packet->raw->op != BOOTREPLY)
781 /* If there's a reject list, make sure this packet's sender isn't
783 for (ap = packet->interface->client->config->reject_list;
785 if (addr_eq(packet->client_addr, ap->addr)) {
786 note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
794 dhcp(struct packet *packet)
796 struct iaddrlist *ap;
797 void (*handler)(struct packet *);
800 switch (packet->packet_type) {
817 /* If there's a reject list, make sure this packet's sender isn't
819 for (ap = packet->interface->client->config->reject_list;
821 if (addr_eq(packet->client_addr, ap->addr)) {
822 note("%s from %s rejected.", type, piaddr(ap->addr));
830 dhcpoffer(struct packet *packet)
832 struct interface_info *ip = packet->interface;
833 struct client_lease *lease, *lp;
835 int arp_timeout_needed, stop_selecting;
836 char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
837 "DHCPOFFER" : "BOOTREPLY";
839 /* If we're not receptive to an offer right now, or if the offer
840 has an unrecognizable transaction id, then just drop it. */
841 if (ip->client->state != S_SELECTING ||
842 packet->interface->client->xid != packet->raw->xid ||
843 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
844 (memcmp(packet->interface->hw_address.haddr,
845 packet->raw->chaddr, packet->raw->hlen)))
848 note("%s from %s", name, piaddr(packet->client_addr));
851 /* If this lease doesn't supply the minimum required parameters,
853 for (i = 0; ip->client->config->required_options[i]; i++) {
854 if (!packet->options[ip->client->config->
855 required_options[i]].len) {
856 note("%s isn't satisfactory.", name);
861 /* If we've already seen this lease, don't record it again. */
862 for (lease = ip->client->offered_leases;
863 lease; lease = lease->next) {
864 if (lease->address.len == sizeof(packet->raw->yiaddr) &&
865 !memcmp(lease->address.iabuf,
866 &packet->raw->yiaddr, lease->address.len)) {
867 debug("%s already seen.", name);
872 lease = packet_to_lease(packet);
874 note("packet_to_lease failed.");
878 /* If this lease was acquired through a BOOTREPLY, record that
880 if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
883 /* Record the medium under which this lease was offered. */
884 lease->medium = ip->client->medium;
886 /* Send out an ARP Request for the offered IP address. */
887 script_init("ARPSEND", lease->medium);
888 script_write_params("check_", lease);
889 /* If the script can't send an ARP request without waiting,
890 we'll be waiting when we do the ARPCHECK, so don't wait now. */
892 arp_timeout_needed = 0;
894 arp_timeout_needed = 2;
896 /* Figure out when we're supposed to stop selecting. */
898 ip->client->first_sending + ip->client->config->select_interval;
900 /* If this is the lease we asked for, put it at the head of the
901 list, and don't mess with the arp request timeout. */
902 if (lease->address.len == ip->client->requested_address.len &&
903 !memcmp(lease->address.iabuf,
904 ip->client->requested_address.iabuf,
905 ip->client->requested_address.len)) {
906 lease->next = ip->client->offered_leases;
907 ip->client->offered_leases = lease;
909 /* If we already have an offer, and arping for this
910 offer would take us past the selection timeout,
911 then don't extend the timeout - just hope for the
913 if (ip->client->offered_leases &&
914 (cur_time + arp_timeout_needed) > stop_selecting)
915 arp_timeout_needed = 0;
917 /* Put the lease at the end of the list. */
919 if (!ip->client->offered_leases)
920 ip->client->offered_leases = lease;
922 for (lp = ip->client->offered_leases; lp->next;
929 /* If we're supposed to stop selecting before we've had time
930 to wait for the ARPREPLY, add some delay to wait for
932 if (stop_selecting - cur_time < arp_timeout_needed)
933 stop_selecting = cur_time + arp_timeout_needed;
935 /* If the selecting interval has expired, go immediately to
936 state_selecting(). Otherwise, time out into
937 state_selecting at the select interval. */
938 if (stop_selecting <= 0)
941 add_timeout(stop_selecting, state_selecting, ip);
942 cancel_timeout(send_discover, ip);
946 /* Allocate a client_lease structure and initialize it from the parameters
947 in the specified packet. */
949 struct client_lease *
950 packet_to_lease(struct packet *packet)
952 struct client_lease *lease;
955 lease = malloc(sizeof(struct client_lease));
958 warning("dhcpoffer: no memory to record lease.");
962 memset(lease, 0, sizeof(*lease));
964 /* Copy the lease options. */
965 for (i = 0; i < 256; i++) {
966 if (packet->options[i].len) {
967 lease->options[i].data =
968 malloc(packet->options[i].len + 1);
969 if (!lease->options[i].data) {
970 warning("dhcpoffer: no memory for option %d", i);
971 free_client_lease(lease);
974 memcpy(lease->options[i].data,
975 packet->options[i].data,
976 packet->options[i].len);
977 lease->options[i].len =
978 packet->options[i].len;
979 lease->options[i].data[lease->options[i].len] =
982 if (!check_option(lease,i)) {
983 /* ignore a bogus lease offer */
984 warning("Invalid lease option - ignoring offer");
985 free_client_lease(lease);
991 lease->address.len = sizeof(packet->raw->yiaddr);
992 memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
994 /* If the server name was filled out, copy it.
995 Do not attempt to validate the server name as a host name.
996 RFC 2131 merely states that sname is NUL-terminated (which do
997 do not assume) and that it is the server's host name. Since
998 the ISC client and server allow arbitrary characters, we do
1000 if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
1001 !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
1002 packet->raw->sname[0]) {
1003 lease->server_name = malloc(DHCP_SNAME_LEN + 1);
1004 if (!lease->server_name) {
1005 warning("dhcpoffer: no memory for server name.");
1006 free_client_lease(lease);
1009 memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
1010 lease->server_name[DHCP_SNAME_LEN]='\0';
1013 /* Ditto for the filename. */
1014 if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
1015 !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
1016 packet->raw->file[0]) {
1017 /* Don't count on the NUL terminator. */
1018 lease->filename = malloc(DHCP_FILE_LEN + 1);
1019 if (!lease->filename) {
1020 warning("dhcpoffer: no memory for filename.");
1021 free_client_lease(lease);
1024 memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
1025 lease->filename[DHCP_FILE_LEN]='\0';
1031 dhcpnak(struct packet *packet)
1033 struct interface_info *ip = packet->interface;
1035 /* If we're not receptive to an offer right now, or if the offer
1036 has an unrecognizable transaction id, then just drop it. */
1037 if (packet->interface->client->xid != packet->raw->xid ||
1038 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
1039 (memcmp(packet->interface->hw_address.haddr,
1040 packet->raw->chaddr, packet->raw->hlen)))
1043 if (ip->client->state != S_REBOOTING &&
1044 ip->client->state != S_REQUESTING &&
1045 ip->client->state != S_RENEWING &&
1046 ip->client->state != S_REBINDING)
1049 note("DHCPNAK from %s", piaddr(packet->client_addr));
1051 if (!ip->client->active) {
1052 note("DHCPNAK with no active lease.\n");
1056 free_client_lease(ip->client->active);
1057 ip->client->active = NULL;
1059 /* Stop sending DHCPREQUEST packets... */
1060 cancel_timeout(send_request, ip);
1062 ip->client->state = S_INIT;
1066 /* Send out a DHCPDISCOVER packet, and set a timeout to send out another
1067 one after the right interval has expired. If we don't get an offer by
1068 the time we reach the panic interval, call the panic function. */
1071 send_discover(void *ipp)
1073 struct interface_info *ip = ipp;
1074 int interval, increase = 1;
1076 /* Figure out how long it's been since we started transmitting. */
1077 interval = cur_time - ip->client->first_sending;
1079 /* If we're past the panic timeout, call the script and tell it
1080 we haven't found anything for this interface yet. */
1081 if (interval > ip->client->config->timeout) {
1086 /* If we're selecting media, try the whole list before doing
1087 the exponential backoff, but if we've already received an
1088 offer, stop looping, because we obviously have it right. */
1089 if (!ip->client->offered_leases &&
1090 ip->client->config->media) {
1093 if (ip->client->medium) {
1094 ip->client->medium = ip->client->medium->next;
1097 if (!ip->client->medium) {
1099 error("No valid media types for %s!", ip->name);
1100 ip->client->medium = ip->client->config->media;
1104 note("Trying medium \"%s\" %d", ip->client->medium->string,
1106 script_init("MEDIUM", ip->client->medium);
1112 * If we're supposed to increase the interval, do so. If it's
1113 * currently zero (i.e., we haven't sent any packets yet), set
1114 * it to one; otherwise, add to it a random number between zero
1115 * and two times itself. On average, this means that it will
1116 * double with every transmission.
1119 if (!ip->client->interval)
1120 ip->client->interval =
1121 ip->client->config->initial_interval;
1123 ip->client->interval += (arc4random() >> 2) %
1124 (2 * ip->client->interval);
1127 /* Don't backoff past cutoff. */
1128 if (ip->client->interval >
1129 ip->client->config->backoff_cutoff)
1130 ip->client->interval =
1131 ((ip->client->config->backoff_cutoff / 2)
1132 + ((arc4random() >> 2) %
1133 ip->client->config->backoff_cutoff));
1134 } else if (!ip->client->interval)
1135 ip->client->interval =
1136 ip->client->config->initial_interval;
1138 /* If the backoff would take us to the panic timeout, just use that
1140 if (cur_time + ip->client->interval >
1141 ip->client->first_sending + ip->client->config->timeout)
1142 ip->client->interval =
1143 (ip->client->first_sending +
1144 ip->client->config->timeout) - cur_time + 1;
1146 /* Record the number of seconds since we started sending. */
1147 if (interval < 65536)
1148 ip->client->packet.secs = htons(interval);
1150 ip->client->packet.secs = htons(65535);
1151 ip->client->secs = ip->client->packet.secs;
1153 note("DHCPDISCOVER on %s to %s port %d interval %d",
1154 ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
1155 ntohs(sockaddr_broadcast.sin_port),
1156 (int)ip->client->interval);
1158 /* Send out a packet. */
1159 (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
1160 inaddr_any, &sockaddr_broadcast, NULL);
1162 add_timeout(cur_time + ip->client->interval, send_discover, ip);
1166 * state_panic gets called if we haven't received any offers in a preset
1167 * amount of time. When this happens, we try to use existing leases
1168 * that haven't yet expired, and failing that, we call the client script
1169 * and hope it can do something.
1172 state_panic(void *ipp)
1174 struct interface_info *ip = ipp;
1175 struct client_lease *loop = ip->client->active;
1176 struct client_lease *lp;
1178 note("No DHCPOFFERS received.");
1180 /* We may not have an active lease, but we may have some
1181 predefined leases that we can try. */
1182 if (!ip->client->active && ip->client->leases)
1185 /* Run through the list of leases and see if one can be used. */
1186 while (ip->client->active) {
1187 if (ip->client->active->expiry > cur_time) {
1188 note("Trying recorded lease %s",
1189 piaddr(ip->client->active->address));
1190 /* Run the client script with the existing
1192 script_init("TIMEOUT",
1193 ip->client->active->medium);
1194 script_write_params("new_", ip->client->active);
1195 if (ip->client->alias)
1196 script_write_params("alias_",
1199 /* If the old lease is still good and doesn't
1200 yet need renewal, go into BOUND state and
1201 timeout at the renewal time. */
1204 ip->client->active->renewal) {
1205 ip->client->state = S_BOUND;
1206 note("bound: renewal in %d seconds.",
1207 (int)(ip->client->active->renewal -
1210 ip->client->active->renewal,
1213 ip->client->state = S_BOUND;
1214 note("bound: immediate renewal.");
1217 reinitialize_interfaces();
1223 /* If there are no other leases, give up. */
1224 if (!ip->client->leases) {
1225 ip->client->leases = ip->client->active;
1226 ip->client->active = NULL;
1231 /* Otherwise, put the active lease at the end of the
1232 lease list, and try another lease.. */
1233 for (lp = ip->client->leases; lp->next; lp = lp->next)
1235 lp->next = ip->client->active;
1237 lp->next->next = NULL;
1238 ip->client->active = ip->client->leases;
1239 ip->client->leases = ip->client->leases->next;
1241 /* If we already tried this lease, we've exhausted the
1242 set of leases, so we might as well give up for
1244 if (ip->client->active == loop)
1247 loop = ip->client->active;
1250 /* No leases were available, or what was available didn't work, so
1251 tell the shell script that we failed to allocate an address,
1252 and try again later. */
1253 note("No working leases in persistent database - sleeping.\n");
1254 script_init("FAIL", NULL);
1255 if (ip->client->alias)
1256 script_write_params("alias_", ip->client->alias);
1258 ip->client->state = S_INIT;
1259 add_timeout(cur_time + ip->client->config->retry_interval, state_init,
1265 send_request(void *ipp)
1267 struct interface_info *ip = ipp;
1268 struct sockaddr_in destination;
1269 struct in_addr from;
1272 /* Figure out how long it's been since we started transmitting. */
1273 interval = cur_time - ip->client->first_sending;
1275 /* If we're in the INIT-REBOOT or REQUESTING state and we're
1276 past the reboot timeout, go to INIT and see if we can
1277 DISCOVER an address... */
1278 /* XXX In the INIT-REBOOT state, if we don't get an ACK, it
1279 means either that we're on a network with no DHCP server,
1280 or that our server is down. In the latter case, assuming
1281 that there is a backup DHCP server, DHCPDISCOVER will get
1282 us a new address, but we could also have successfully
1283 reused our old address. In the former case, we're hosed
1284 anyway. This is not a win-prone situation. */
1285 if ((ip->client->state == S_REBOOTING ||
1286 ip->client->state == S_REQUESTING) &&
1287 interval > ip->client->config->reboot_timeout) {
1289 ip->client->state = S_INIT;
1290 cancel_timeout(send_request, ip);
1295 /* If we're in the reboot state, make sure the media is set up
1297 if (ip->client->state == S_REBOOTING &&
1298 !ip->client->medium &&
1299 ip->client->active->medium ) {
1300 script_init("MEDIUM", ip->client->active->medium);
1302 /* If the medium we chose won't fly, go to INIT state. */
1306 /* Record the medium. */
1307 ip->client->medium = ip->client->active->medium;
1310 /* If the lease has expired, relinquish the address and go back
1311 to the INIT state. */
1312 if (ip->client->state != S_REQUESTING &&
1313 cur_time > ip->client->active->expiry) {
1314 /* Run the client script with the new parameters. */
1315 script_init("EXPIRE", NULL);
1316 script_write_params("old_", ip->client->active);
1317 if (ip->client->alias)
1318 script_write_params("alias_", ip->client->alias);
1321 /* Now do a preinit on the interface so that we can
1322 discover a new address. */
1323 script_init("PREINIT", NULL);
1324 if (ip->client->alias)
1325 script_write_params("alias_", ip->client->alias);
1328 ip->client->state = S_INIT;
1333 /* Do the exponential backoff... */
1334 if (!ip->client->interval)
1335 ip->client->interval = ip->client->config->initial_interval;
1337 ip->client->interval += ((arc4random() >> 2) %
1338 (2 * ip->client->interval));
1340 /* Don't backoff past cutoff. */
1341 if (ip->client->interval >
1342 ip->client->config->backoff_cutoff)
1343 ip->client->interval =
1344 ((ip->client->config->backoff_cutoff / 2) +
1345 ((arc4random() >> 2) % ip->client->interval));
1347 /* If the backoff would take us to the expiry time, just set the
1348 timeout to the expiry time. */
1349 if (ip->client->state != S_REQUESTING &&
1350 cur_time + ip->client->interval >
1351 ip->client->active->expiry)
1352 ip->client->interval =
1353 ip->client->active->expiry - cur_time + 1;
1355 /* If the lease T2 time has elapsed, or if we're not yet bound,
1356 broadcast the DHCPREQUEST rather than unicasting. */
1357 memset(&destination, 0, sizeof(destination));
1358 if (ip->client->state == S_REQUESTING ||
1359 ip->client->state == S_REBOOTING ||
1360 cur_time > ip->client->active->rebind)
1361 destination.sin_addr.s_addr = INADDR_BROADCAST;
1363 memcpy(&destination.sin_addr.s_addr,
1364 ip->client->destination.iabuf,
1365 sizeof(destination.sin_addr.s_addr));
1366 destination.sin_port = htons(REMOTE_PORT);
1367 destination.sin_family = AF_INET;
1368 destination.sin_len = sizeof(destination);
1370 if (ip->client->state != S_REQUESTING)
1371 memcpy(&from, ip->client->active->address.iabuf,
1374 from.s_addr = INADDR_ANY;
1376 /* Record the number of seconds since we started sending. */
1377 if (ip->client->state == S_REQUESTING)
1378 ip->client->packet.secs = ip->client->secs;
1380 if (interval < 65536)
1381 ip->client->packet.secs = htons(interval);
1383 ip->client->packet.secs = htons(65535);
1386 note("DHCPREQUEST on %s to %s port %d", ip->name,
1387 inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
1389 /* Send out a packet. */
1390 (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
1391 from, &destination, NULL);
1393 add_timeout(cur_time + ip->client->interval, send_request, ip);
1397 send_decline(void *ipp)
1399 struct interface_info *ip = ipp;
1401 note("DHCPDECLINE on %s to %s port %d", ip->name,
1402 inet_ntoa(sockaddr_broadcast.sin_addr),
1403 ntohs(sockaddr_broadcast.sin_port));
1405 /* Send out a packet. */
1406 (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
1407 inaddr_any, &sockaddr_broadcast, NULL);
1411 make_discover(struct interface_info *ip, struct client_lease *lease)
1413 unsigned char discover = DHCPDISCOVER;
1414 struct tree_cache *options[256];
1415 struct tree_cache option_elements[256];
1418 memset(option_elements, 0, sizeof(option_elements));
1419 memset(options, 0, sizeof(options));
1420 memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1422 /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
1423 i = DHO_DHCP_MESSAGE_TYPE;
1424 options[i] = &option_elements[i];
1425 options[i]->value = &discover;
1426 options[i]->len = sizeof(discover);
1427 options[i]->buf_size = sizeof(discover);
1428 options[i]->timeout = 0xFFFFFFFF;
1430 /* Request the options we want */
1431 i = DHO_DHCP_PARAMETER_REQUEST_LIST;
1432 options[i] = &option_elements[i];
1433 options[i]->value = ip->client->config->requested_options;
1434 options[i]->len = ip->client->config->requested_option_count;
1435 options[i]->buf_size =
1436 ip->client->config->requested_option_count;
1437 options[i]->timeout = 0xFFFFFFFF;
1439 /* If we had an address, try to get it again. */
1441 ip->client->requested_address = lease->address;
1442 i = DHO_DHCP_REQUESTED_ADDRESS;
1443 options[i] = &option_elements[i];
1444 options[i]->value = lease->address.iabuf;
1445 options[i]->len = lease->address.len;
1446 options[i]->buf_size = lease->address.len;
1447 options[i]->timeout = 0xFFFFFFFF;
1449 ip->client->requested_address.len = 0;
1451 /* Send any options requested in the config file. */
1452 for (i = 0; i < 256; i++)
1454 ip->client->config->send_options[i].data) {
1455 options[i] = &option_elements[i];
1457 ip->client->config->send_options[i].data;
1459 ip->client->config->send_options[i].len;
1460 options[i]->buf_size =
1461 ip->client->config->send_options[i].len;
1462 options[i]->timeout = 0xFFFFFFFF;
1465 /* send host name if not set via config file. */
1466 char hostname[_POSIX_HOST_NAME_MAX+1];
1467 if (!options[DHO_HOST_NAME]) {
1468 if (gethostname(hostname, sizeof(hostname)) == 0) {
1470 char* posDot = strchr(hostname, '.');
1472 len = posDot - hostname;
1474 len = strlen(hostname);
1475 options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME];
1476 options[DHO_HOST_NAME]->value = hostname;
1477 options[DHO_HOST_NAME]->len = len;
1478 options[DHO_HOST_NAME]->buf_size = len;
1479 options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF;
1483 /* set unique client identifier */
1484 char client_ident[sizeof(struct hardware)];
1485 if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
1486 int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
1487 ip->hw_address.hlen : sizeof(client_ident)-1;
1488 client_ident[0] = ip->hw_address.htype;
1489 memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
1490 options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
1491 options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
1492 options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
1493 options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1;
1494 options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF;
1497 /* Set up the option buffer... */
1498 ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1499 options, 0, 0, 0, NULL, 0);
1500 if (ip->client->packet_length < BOOTP_MIN_LEN)
1501 ip->client->packet_length = BOOTP_MIN_LEN;
1503 ip->client->packet.op = BOOTREQUEST;
1504 ip->client->packet.htype = ip->hw_address.htype;
1505 ip->client->packet.hlen = ip->hw_address.hlen;
1506 ip->client->packet.hops = 0;
1507 ip->client->packet.xid = arc4random();
1508 ip->client->packet.secs = 0; /* filled in by send_discover. */
1509 ip->client->packet.flags = 0;
1511 memset(&(ip->client->packet.ciaddr),
1512 0, sizeof(ip->client->packet.ciaddr));
1513 memset(&(ip->client->packet.yiaddr),
1514 0, sizeof(ip->client->packet.yiaddr));
1515 memset(&(ip->client->packet.siaddr),
1516 0, sizeof(ip->client->packet.siaddr));
1517 memset(&(ip->client->packet.giaddr),
1518 0, sizeof(ip->client->packet.giaddr));
1519 memcpy(ip->client->packet.chaddr,
1520 ip->hw_address.haddr, ip->hw_address.hlen);
1525 make_request(struct interface_info *ip, struct client_lease * lease)
1527 unsigned char request = DHCPREQUEST;
1528 struct tree_cache *options[256];
1529 struct tree_cache option_elements[256];
1532 memset(options, 0, sizeof(options));
1533 memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1535 /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
1536 i = DHO_DHCP_MESSAGE_TYPE;
1537 options[i] = &option_elements[i];
1538 options[i]->value = &request;
1539 options[i]->len = sizeof(request);
1540 options[i]->buf_size = sizeof(request);
1541 options[i]->timeout = 0xFFFFFFFF;
1543 /* Request the options we want */
1544 i = DHO_DHCP_PARAMETER_REQUEST_LIST;
1545 options[i] = &option_elements[i];
1546 options[i]->value = ip->client->config->requested_options;
1547 options[i]->len = ip->client->config->requested_option_count;
1548 options[i]->buf_size =
1549 ip->client->config->requested_option_count;
1550 options[i]->timeout = 0xFFFFFFFF;
1552 /* If we are requesting an address that hasn't yet been assigned
1553 to us, use the DHCP Requested Address option. */
1554 if (ip->client->state == S_REQUESTING) {
1555 /* Send back the server identifier... */
1556 i = DHO_DHCP_SERVER_IDENTIFIER;
1557 options[i] = &option_elements[i];
1558 options[i]->value = lease->options[i].data;
1559 options[i]->len = lease->options[i].len;
1560 options[i]->buf_size = lease->options[i].len;
1561 options[i]->timeout = 0xFFFFFFFF;
1563 if (ip->client->state == S_REQUESTING ||
1564 ip->client->state == S_REBOOTING) {
1565 ip->client->requested_address = lease->address;
1566 i = DHO_DHCP_REQUESTED_ADDRESS;
1567 options[i] = &option_elements[i];
1568 options[i]->value = lease->address.iabuf;
1569 options[i]->len = lease->address.len;
1570 options[i]->buf_size = lease->address.len;
1571 options[i]->timeout = 0xFFFFFFFF;
1573 ip->client->requested_address.len = 0;
1575 /* Send any options requested in the config file. */
1576 for (i = 0; i < 256; i++)
1578 ip->client->config->send_options[i].data) {
1579 options[i] = &option_elements[i];
1581 ip->client->config->send_options[i].data;
1583 ip->client->config->send_options[i].len;
1584 options[i]->buf_size =
1585 ip->client->config->send_options[i].len;
1586 options[i]->timeout = 0xFFFFFFFF;
1589 /* send host name if not set via config file. */
1590 char hostname[_POSIX_HOST_NAME_MAX+1];
1591 if (!options[DHO_HOST_NAME]) {
1592 if (gethostname(hostname, sizeof(hostname)) == 0) {
1594 char* posDot = strchr(hostname, '.');
1596 len = posDot - hostname;
1598 len = strlen(hostname);
1599 options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME];
1600 options[DHO_HOST_NAME]->value = hostname;
1601 options[DHO_HOST_NAME]->len = len;
1602 options[DHO_HOST_NAME]->buf_size = len;
1603 options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF;
1607 /* set unique client identifier */
1608 char client_ident[sizeof(struct hardware)];
1609 if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) {
1610 int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
1611 ip->hw_address.hlen : sizeof(client_ident)-1;
1612 client_ident[0] = ip->hw_address.htype;
1613 memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
1614 options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
1615 options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
1616 options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
1617 options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1;
1618 options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF;
1621 /* Set up the option buffer... */
1622 ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1623 options, 0, 0, 0, NULL, 0);
1624 if (ip->client->packet_length < BOOTP_MIN_LEN)
1625 ip->client->packet_length = BOOTP_MIN_LEN;
1627 ip->client->packet.op = BOOTREQUEST;
1628 ip->client->packet.htype = ip->hw_address.htype;
1629 ip->client->packet.hlen = ip->hw_address.hlen;
1630 ip->client->packet.hops = 0;
1631 ip->client->packet.xid = ip->client->xid;
1632 ip->client->packet.secs = 0; /* Filled in by send_request. */
1634 /* If we own the address we're requesting, put it in ciaddr;
1635 otherwise set ciaddr to zero. */
1636 if (ip->client->state == S_BOUND ||
1637 ip->client->state == S_RENEWING ||
1638 ip->client->state == S_REBINDING) {
1639 memcpy(&ip->client->packet.ciaddr,
1640 lease->address.iabuf, lease->address.len);
1641 ip->client->packet.flags = 0;
1643 memset(&ip->client->packet.ciaddr, 0,
1644 sizeof(ip->client->packet.ciaddr));
1645 ip->client->packet.flags = 0;
1648 memset(&ip->client->packet.yiaddr, 0,
1649 sizeof(ip->client->packet.yiaddr));
1650 memset(&ip->client->packet.siaddr, 0,
1651 sizeof(ip->client->packet.siaddr));
1652 memset(&ip->client->packet.giaddr, 0,
1653 sizeof(ip->client->packet.giaddr));
1654 memcpy(ip->client->packet.chaddr,
1655 ip->hw_address.haddr, ip->hw_address.hlen);
1659 make_decline(struct interface_info *ip, struct client_lease *lease)
1661 struct tree_cache *options[256], message_type_tree;
1662 struct tree_cache requested_address_tree;
1663 struct tree_cache server_id_tree, client_id_tree;
1664 unsigned char decline = DHCPDECLINE;
1667 memset(options, 0, sizeof(options));
1668 memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1670 /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
1671 i = DHO_DHCP_MESSAGE_TYPE;
1672 options[i] = &message_type_tree;
1673 options[i]->value = &decline;
1674 options[i]->len = sizeof(decline);
1675 options[i]->buf_size = sizeof(decline);
1676 options[i]->timeout = 0xFFFFFFFF;
1678 /* Send back the server identifier... */
1679 i = DHO_DHCP_SERVER_IDENTIFIER;
1680 options[i] = &server_id_tree;
1681 options[i]->value = lease->options[i].data;
1682 options[i]->len = lease->options[i].len;
1683 options[i]->buf_size = lease->options[i].len;
1684 options[i]->timeout = 0xFFFFFFFF;
1686 /* Send back the address we're declining. */
1687 i = DHO_DHCP_REQUESTED_ADDRESS;
1688 options[i] = &requested_address_tree;
1689 options[i]->value = lease->address.iabuf;
1690 options[i]->len = lease->address.len;
1691 options[i]->buf_size = lease->address.len;
1692 options[i]->timeout = 0xFFFFFFFF;
1694 /* Send the uid if the user supplied one. */
1695 i = DHO_DHCP_CLIENT_IDENTIFIER;
1696 if (ip->client->config->send_options[i].len) {
1697 options[i] = &client_id_tree;
1698 options[i]->value = ip->client->config->send_options[i].data;
1699 options[i]->len = ip->client->config->send_options[i].len;
1700 options[i]->buf_size = ip->client->config->send_options[i].len;
1701 options[i]->timeout = 0xFFFFFFFF;
1705 /* Set up the option buffer... */
1706 ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1707 options, 0, 0, 0, NULL, 0);
1708 if (ip->client->packet_length < BOOTP_MIN_LEN)
1709 ip->client->packet_length = BOOTP_MIN_LEN;
1711 ip->client->packet.op = BOOTREQUEST;
1712 ip->client->packet.htype = ip->hw_address.htype;
1713 ip->client->packet.hlen = ip->hw_address.hlen;
1714 ip->client->packet.hops = 0;
1715 ip->client->packet.xid = ip->client->xid;
1716 ip->client->packet.secs = 0; /* Filled in by send_request. */
1717 ip->client->packet.flags = 0;
1719 /* ciaddr must always be zero. */
1720 memset(&ip->client->packet.ciaddr, 0,
1721 sizeof(ip->client->packet.ciaddr));
1722 memset(&ip->client->packet.yiaddr, 0,
1723 sizeof(ip->client->packet.yiaddr));
1724 memset(&ip->client->packet.siaddr, 0,
1725 sizeof(ip->client->packet.siaddr));
1726 memset(&ip->client->packet.giaddr, 0,
1727 sizeof(ip->client->packet.giaddr));
1728 memcpy(ip->client->packet.chaddr,
1729 ip->hw_address.haddr, ip->hw_address.hlen);
1733 free_client_lease(struct client_lease *lease)
1737 if (lease->server_name)
1738 free(lease->server_name);
1739 if (lease->filename)
1740 free(lease->filename);
1741 for (i = 0; i < 256; i++) {
1742 if (lease->options[i].len)
1743 free(lease->options[i].data);
1751 rewrite_client_leases(void)
1753 struct client_lease *lp;
1756 leaseFile = fopen(path_dhclient_db, "w");
1758 error("can't create %s: %m", path_dhclient_db);
1764 for (lp = ifi->client->leases; lp; lp = lp->next)
1765 write_client_lease(ifi, lp, 1);
1766 if (ifi->client->active)
1767 write_client_lease(ifi, ifi->client->active, 1);
1770 ftruncate(fileno(leaseFile), ftello(leaseFile));
1771 fsync(fileno(leaseFile));
1775 write_client_lease(struct interface_info *ip, struct client_lease *lease,
1778 static int leases_written;
1783 if (leases_written++ > 20) {
1784 rewrite_client_leases();
1789 /* If the lease came from the config file, we don't need to stash
1790 a copy in the lease database. */
1791 if (lease->is_static)
1794 if (!leaseFile) { /* XXX */
1795 leaseFile = fopen(path_dhclient_db, "w");
1797 error("can't create %s: %m", path_dhclient_db);
1800 fprintf(leaseFile, "lease {\n");
1801 if (lease->is_bootp)
1802 fprintf(leaseFile, " bootp;\n");
1803 fprintf(leaseFile, " interface \"%s\";\n", ip->name);
1804 fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address));
1805 if (lease->filename)
1806 fprintf(leaseFile, " filename \"%s\";\n", lease->filename);
1807 if (lease->server_name)
1808 fprintf(leaseFile, " server-name \"%s\";\n",
1809 lease->server_name);
1811 fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string);
1812 for (i = 0; i < 256; i++)
1813 if (lease->options[i].len)
1814 fprintf(leaseFile, " option %s %s;\n",
1815 dhcp_options[i].name,
1816 pretty_print_option(i, lease->options[i].data,
1817 lease->options[i].len, 1, 1));
1819 t = gmtime(&lease->renewal);
1820 fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n",
1821 t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1822 t->tm_hour, t->tm_min, t->tm_sec);
1823 t = gmtime(&lease->rebind);
1824 fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n",
1825 t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1826 t->tm_hour, t->tm_min, t->tm_sec);
1827 t = gmtime(&lease->expiry);
1828 fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n",
1829 t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1830 t->tm_hour, t->tm_min, t->tm_sec);
1831 fprintf(leaseFile, "}\n");
1836 script_init(char *reason, struct string_list *medium)
1838 size_t len, mediumlen = 0;
1839 struct imsg_hdr hdr;
1843 if (medium != NULL && medium->string != NULL)
1844 mediumlen = strlen(medium->string);
1846 hdr.code = IMSG_SCRIPT_INIT;
1847 hdr.len = sizeof(struct imsg_hdr) +
1848 sizeof(size_t) + mediumlen +
1849 sizeof(size_t) + strlen(reason);
1851 if ((buf = buf_open(hdr.len)) == NULL)
1852 error("buf_open: %m");
1855 errs += buf_add(buf, &hdr, sizeof(hdr));
1856 errs += buf_add(buf, &mediumlen, sizeof(mediumlen));
1858 errs += buf_add(buf, medium->string, mediumlen);
1859 len = strlen(reason);
1860 errs += buf_add(buf, &len, sizeof(len));
1861 errs += buf_add(buf, reason, len);
1864 error("buf_add: %m");
1866 if (buf_close(privfd, buf) == -1)
1867 error("buf_close: %m");
1871 priv_script_init(char *reason, char *medium)
1873 struct interface_info *ip = ifi;
1876 ip->client->scriptEnvsize = 100;
1877 if (ip->client->scriptEnv == NULL)
1878 ip->client->scriptEnv =
1879 malloc(ip->client->scriptEnvsize * sizeof(char *));
1880 if (ip->client->scriptEnv == NULL)
1881 error("script_init: no memory for environment");
1883 ip->client->scriptEnv[0] = strdup(CLIENT_PATH);
1884 if (ip->client->scriptEnv[0] == NULL)
1885 error("script_init: no memory for environment");
1887 ip->client->scriptEnv[1] = NULL;
1889 script_set_env(ip->client, "", "interface", ip->name);
1892 script_set_env(ip->client, "", "medium", medium);
1894 script_set_env(ip->client, "", "reason", reason);
1899 priv_script_write_params(char *prefix, struct client_lease *lease)
1901 struct interface_info *ip = ifi;
1902 u_int8_t dbuf[1500], *dp = NULL;
1906 script_set_env(ip->client, prefix, "ip_address",
1907 piaddr(lease->address));
1909 if (ip->client->config->default_actions[DHO_SUBNET_MASK] ==
1911 dp = ip->client->config->defaults[DHO_SUBNET_MASK].data;
1912 len = ip->client->config->defaults[DHO_SUBNET_MASK].len;
1914 dp = lease->options[DHO_SUBNET_MASK].data;
1915 len = lease->options[DHO_SUBNET_MASK].len;
1917 if (len && (len < sizeof(lease->address.iabuf))) {
1918 struct iaddr netmask, subnet, broadcast;
1920 memcpy(netmask.iabuf, dp, len);
1922 subnet = subnet_number(lease->address, netmask);
1924 script_set_env(ip->client, prefix, "network_number",
1926 if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
1927 broadcast = broadcast_addr(subnet, netmask);
1929 script_set_env(ip->client, prefix,
1930 "broadcast_address",
1936 if (lease->filename)
1937 script_set_env(ip->client, prefix, "filename", lease->filename);
1938 if (lease->server_name)
1939 script_set_env(ip->client, prefix, "server_name",
1940 lease->server_name);
1941 for (i = 0; i < 256; i++) {
1944 if (ip->client->config->defaults[i].len) {
1945 if (lease->options[i].len) {
1947 ip->client->config->default_actions[i]) {
1948 case ACTION_DEFAULT:
1949 dp = lease->options[i].data;
1950 len = lease->options[i].len;
1952 case ACTION_SUPERSEDE:
1955 config->defaults[i].data;
1957 config->defaults[i].len;
1959 case ACTION_PREPEND:
1961 config->defaults[i].len +
1962 lease->options[i].len;
1963 if (len > sizeof(dbuf)) {
1964 warning("no space to %s %s",
1966 dhcp_options[i].name);
1972 config->defaults[i].data,
1974 config->defaults[i].len);
1975 memcpy(dp + ip->client->
1976 config->defaults[i].len,
1977 lease->options[i].data,
1978 lease->options[i].len);
1983 config->defaults[i].len +
1984 lease->options[i].len;
1985 if (len > sizeof(dbuf)) {
1986 warning("no space to %s %s",
1988 dhcp_options[i].name);
1993 lease->options[i].data,
1994 lease->options[i].len);
1995 memcpy(dp + lease->options[i].len,
1997 config->defaults[i].data,
1999 config->defaults[i].len);
2004 config->defaults[i].data;
2006 config->defaults[i].len;
2008 } else if (lease->options[i].len) {
2009 len = lease->options[i].len;
2010 dp = lease->options[i].data;
2017 if (dhcp_option_ev_name(name, sizeof(name),
2019 script_set_env(ip->client, prefix, name,
2020 pretty_print_option(i, dp, len, 0, 0));
2023 snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
2024 script_set_env(ip->client, prefix, "expiry", tbuf);
2028 script_write_params(char *prefix, struct client_lease *lease)
2030 size_t fn_len = 0, sn_len = 0, pr_len = 0;
2031 struct imsg_hdr hdr;
2035 if (lease->filename != NULL)
2036 fn_len = strlen(lease->filename);
2037 if (lease->server_name != NULL)
2038 sn_len = strlen(lease->server_name);
2040 pr_len = strlen(prefix);
2042 hdr.code = IMSG_SCRIPT_WRITE_PARAMS;
2043 hdr.len = sizeof(hdr) + sizeof(struct client_lease) +
2044 sizeof(size_t) + fn_len + sizeof(size_t) + sn_len +
2045 sizeof(size_t) + pr_len;
2047 for (i = 0; i < 256; i++)
2048 hdr.len += sizeof(int) + lease->options[i].len;
2050 scripttime = time(NULL);
2052 if ((buf = buf_open(hdr.len)) == NULL)
2053 error("buf_open: %m");
2056 errs += buf_add(buf, &hdr, sizeof(hdr));
2057 errs += buf_add(buf, lease, sizeof(struct client_lease));
2058 errs += buf_add(buf, &fn_len, sizeof(fn_len));
2059 errs += buf_add(buf, lease->filename, fn_len);
2060 errs += buf_add(buf, &sn_len, sizeof(sn_len));
2061 errs += buf_add(buf, lease->server_name, sn_len);
2062 errs += buf_add(buf, &pr_len, sizeof(pr_len));
2063 errs += buf_add(buf, prefix, pr_len);
2065 for (i = 0; i < 256; i++) {
2066 errs += buf_add(buf, &lease->options[i].len,
2067 sizeof(lease->options[i].len));
2068 errs += buf_add(buf, lease->options[i].data,
2069 lease->options[i].len);
2073 error("buf_add: %m");
2075 if (buf_close(privfd, buf) == -1)
2076 error("buf_close: %m");
2082 struct imsg_hdr hdr;
2086 scripttime = time(NULL);
2088 hdr.code = IMSG_SCRIPT_GO;
2089 hdr.len = sizeof(struct imsg_hdr);
2091 if ((buf = buf_open(hdr.len)) == NULL)
2092 error("buf_open: %m");
2094 if (buf_add(buf, &hdr, sizeof(hdr)))
2095 error("buf_add: %m");
2097 if (buf_close(privfd, buf) == -1)
2098 error("buf_close: %m");
2100 bzero(&hdr, sizeof(hdr));
2101 buf_read(privfd, &hdr, sizeof(hdr));
2102 if (hdr.code != IMSG_SCRIPT_GO_RET)
2103 error("unexpected msg type %u", hdr.code);
2104 if (hdr.len != sizeof(hdr) + sizeof(int))
2105 error("received corrupted message");
2106 buf_read(privfd, &ret, sizeof(ret));
2112 priv_script_go(void)
2114 char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI";
2115 static char client_path[] = CLIENT_PATH;
2116 struct interface_info *ip = ifi;
2117 int pid, wpid, wstatus;
2119 scripttime = time(NULL);
2122 scriptName = ip->client->config->script_name;
2123 envp = ip->client->scriptEnv;
2125 scriptName = top_level_config.script_name;
2127 epp[1] = client_path;
2132 argv[0] = scriptName;
2141 wpid = wait(&wstatus);
2142 } while (wpid != pid && wpid > 0);
2148 execve(scriptName, argv, envp);
2149 error("execve (%s, ...): %m", scriptName);
2153 script_flush_env(ip->client);
2155 return (wstatus & 0xff);
2159 script_set_env(struct client_state *client, const char *prefix,
2160 const char *name, const char *value)
2164 namelen = strlen(name);
2166 for (i = 0; client->scriptEnv[i]; i++)
2167 if (strncmp(client->scriptEnv[i], name, namelen) == 0 &&
2168 client->scriptEnv[i][namelen] == '=')
2171 if (client->scriptEnv[i])
2172 /* Reuse the slot. */
2173 free(client->scriptEnv[i]);
2175 /* New variable. Expand if necessary. */
2176 if (i >= client->scriptEnvsize - 1) {
2177 char **newscriptEnv;
2178 int newscriptEnvsize = client->scriptEnvsize + 50;
2180 newscriptEnv = realloc(client->scriptEnv,
2182 if (newscriptEnv == NULL) {
2183 free(client->scriptEnv);
2184 client->scriptEnv = NULL;
2185 client->scriptEnvsize = 0;
2186 error("script_set_env: no memory for variable");
2188 client->scriptEnv = newscriptEnv;
2189 client->scriptEnvsize = newscriptEnvsize;
2191 /* need to set the NULL pointer at end of array beyond
2193 client->scriptEnv[i + 1] = NULL;
2195 /* Allocate space and format the variable in the appropriate slot. */
2196 client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 +
2198 if (client->scriptEnv[i] == NULL)
2199 error("script_set_env: no memory for variable assignment");
2201 /* No `` or $() command substitution allowed in environment values! */
2202 for (j=0; j < strlen(value); j++)
2206 error("illegal character (%c) in value '%s'", value[j],
2210 snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) +
2211 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value);
2215 script_flush_env(struct client_state *client)
2219 for (i = 0; client->scriptEnv[i]; i++) {
2220 free(client->scriptEnv[i]);
2221 client->scriptEnv[i] = NULL;
2223 client->scriptEnvsize = 0;
2227 dhcp_option_ev_name(char *buf, size_t buflen, struct option *option)
2231 for (i = 0; option->name[i]; i++) {
2232 if (i + 1 == buflen)
2234 if (option->name[i] == '-')
2237 buf[i] = option->name[i];
2247 static int state = 0;
2249 if (no_daemon || state)
2254 /* Stop logging to stderr... */
2257 if (daemon(1, 0) == -1)
2260 /* we are chrooted, daemon(3) fails to open /dev/null */
2262 dup2(nullfd, STDIN_FILENO);
2263 dup2(nullfd, STDOUT_FILENO);
2264 dup2(nullfd, STDERR_FILENO);
2271 check_option(struct client_lease *l, int option)
2276 /* we use this, since this is what gets passed to dhclient-script */
2278 opbuf = pretty_print_option(option, l->options[option].data,
2279 l->options[option].len, 0, 0);
2281 sbuf = option_as_string(option, l->options[option].data,
2282 l->options[option].len);
2285 case DHO_SUBNET_MASK:
2286 case DHO_TIME_SERVERS:
2287 case DHO_NAME_SERVERS:
2289 case DHO_DOMAIN_NAME_SERVERS:
2290 case DHO_LOG_SERVERS:
2291 case DHO_COOKIE_SERVERS:
2292 case DHO_LPR_SERVERS:
2293 case DHO_IMPRESS_SERVERS:
2294 case DHO_RESOURCE_LOCATION_SERVERS:
2295 case DHO_SWAP_SERVER:
2296 case DHO_BROADCAST_ADDRESS:
2297 case DHO_NIS_SERVERS:
2298 case DHO_NTP_SERVERS:
2299 case DHO_NETBIOS_NAME_SERVERS:
2300 case DHO_NETBIOS_DD_SERVER:
2301 case DHO_FONT_SERVERS:
2302 case DHO_DHCP_SERVER_IDENTIFIER:
2303 case DHO_SMTP_SERVER:
2304 case DHO_POP_SERVER:
2305 case DHO_NNTP_SERVER:
2306 case DHO_WWW_SERVER:
2307 case DHO_FINGER_SERVER:
2308 case DHO_IRC_SERVER:
2309 if (!ipv4addrs(opbuf)) {
2310 warning("Invalid IP address in option: %s", opbuf);
2315 case DHO_NIS_DOMAIN:
2316 if (!res_hnok(sbuf)) {
2317 warning("Bogus Host Name option %d: %s (%s)", option,
2319 l->options[option].len = 0;
2320 free(l->options[option].data);
2324 case DHO_DOMAIN_NAME:
2325 if (!res_hnok(sbuf)) {
2326 if (!check_search(sbuf)) {
2327 warning("Bogus domain search list %d: %s (%s)",
2328 option, sbuf, opbuf);
2329 l->options[option].len = 0;
2330 free(l->options[option].data);
2335 case DHO_TIME_OFFSET:
2337 case DHO_MERIT_DUMP:
2339 case DHO_EXTENSIONS_PATH:
2340 case DHO_IP_FORWARDING:
2341 case DHO_NON_LOCAL_SOURCE_ROUTING:
2342 case DHO_POLICY_FILTER:
2343 case DHO_MAX_DGRAM_REASSEMBLY:
2344 case DHO_DEFAULT_IP_TTL:
2345 case DHO_PATH_MTU_AGING_TIMEOUT:
2346 case DHO_PATH_MTU_PLATEAU_TABLE:
2347 case DHO_INTERFACE_MTU:
2348 case DHO_ALL_SUBNETS_LOCAL:
2349 case DHO_PERFORM_MASK_DISCOVERY:
2350 case DHO_MASK_SUPPLIER:
2351 case DHO_ROUTER_DISCOVERY:
2352 case DHO_ROUTER_SOLICITATION_ADDRESS:
2353 case DHO_STATIC_ROUTES:
2354 case DHO_TRAILER_ENCAPSULATION:
2355 case DHO_ARP_CACHE_TIMEOUT:
2356 case DHO_IEEE802_3_ENCAPSULATION:
2357 case DHO_DEFAULT_TCP_TTL:
2358 case DHO_TCP_KEEPALIVE_INTERVAL:
2359 case DHO_TCP_KEEPALIVE_GARBAGE:
2360 case DHO_VENDOR_ENCAPSULATED_OPTIONS:
2361 case DHO_NETBIOS_NODE_TYPE:
2362 case DHO_NETBIOS_SCOPE:
2363 case DHO_X_DISPLAY_MANAGER:
2364 case DHO_DHCP_REQUESTED_ADDRESS:
2365 case DHO_DHCP_LEASE_TIME:
2366 case DHO_DHCP_OPTION_OVERLOAD:
2367 case DHO_DHCP_MESSAGE_TYPE:
2368 case DHO_DHCP_PARAMETER_REQUEST_LIST:
2369 case DHO_DHCP_MESSAGE:
2370 case DHO_DHCP_MAX_MESSAGE_SIZE:
2371 case DHO_DHCP_RENEWAL_TIME:
2372 case DHO_DHCP_REBINDING_TIME:
2373 case DHO_DHCP_CLASS_IDENTIFIER:
2374 case DHO_DHCP_CLIENT_IDENTIFIER:
2375 case DHO_DHCP_USER_CLASS_ID:
2379 warning("unknown dhcp option value 0x%x", option);
2380 return (unknown_ok);
2385 res_hnok(const char *dn)
2387 int pch = PERIOD, ch = *dn++;
2389 while (ch != '\0') {
2392 if (periodchar(ch)) {
2394 } else if (periodchar(pch)) {
2395 if (!borderchar(ch))
2397 } else if (periodchar(nch) || nch == '\0') {
2398 if (!borderchar(ch))
2401 if (!middlechar(ch))
2410 check_search(const char *srch)
2412 int pch = PERIOD, ch = *srch++;
2415 /* 256 char limit re resolv.conf(5) */
2416 if (strlen(srch) > 256)
2419 while (whitechar(ch))
2422 while (ch != '\0') {
2425 if (periodchar(ch) || whitechar(ch)) {
2427 } else if (periodchar(pch)) {
2428 if (!borderchar(ch))
2430 } else if (periodchar(nch) || nch == '\0') {
2431 if (!borderchar(ch))
2434 if (!middlechar(ch))
2437 if (!whitechar(ch)) {
2440 while (whitechar(nch)) {
2449 /* 6 domain limit re resolv.conf(5) */
2455 /* Does buf consist only of dotted decimal ipv4 addrs?
2456 * return how many if so,
2457 * otherwise, return 0
2460 ipv4addrs(char * buf)
2465 while (inet_aton(buf, &jnk) == 1){
2467 while (periodchar(*buf) || digitchar(*buf))
2479 option_as_string(unsigned int code, unsigned char *data, int len)
2481 static char optbuf[32768]; /* XXX */
2483 int opleft = sizeof(optbuf);
2484 unsigned char *dp = data;
2487 error("option_as_string: bad code %d", code);
2489 for (; dp < data + len; dp++) {
2490 if (!isascii(*dp) || !isprint(*dp)) {
2491 if (dp + 1 != data + len || *dp != 0) {
2492 snprintf(op, opleft, "\\%03o", *dp);
2496 } else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
2497 *dp == '`' || *dp == '\\') {
2511 warning("dhcp option too large");
2516 fork_privchld(int fd, int fd2)
2518 struct pollfd pfd[1];
2523 error("cannot fork");
2530 setproctitle("%s [priv]", ifi->name);
2532 dup2(nullfd, STDIN_FILENO);
2533 dup2(nullfd, STDOUT_FILENO);
2534 dup2(nullfd, STDERR_FILENO);
2540 pfd[0].events = POLLIN;
2541 if ((nfds = poll(pfd, 1, INFTIM)) == -1)
2543 error("poll error");
2545 if (nfds == 0 || !(pfd[0].revents & POLLIN))