aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrooks Davis <brooks@FreeBSD.org>2005-06-07 04:05:09 +0000
committerBrooks Davis <brooks@FreeBSD.org>2005-06-07 04:05:09 +0000
commit47c0859616bfefdd59ce6bbd8dd46882cef4527a (patch)
tree3bf6776f442e91d5582a49f3dce940772d56aaed
parenta9c7144e7ac71a1a3ffaedcde30bf1e0a82f98bd (diff)
downloadsrc-47c0859616bfefdd59ce6bbd8dd46882cef4527a.tar.gz
src-47c0859616bfefdd59ce6bbd8dd46882cef4527a.zip
Import the OpenBSD dhclient as shipped with OpenBSD-3.7 (the tag
OPENBSD_3_7).
Notes
Notes: svn path=/vendor/OpenBSD/dist/; revision=147072
-rw-r--r--sbin/dhclient/Makefile58
-rw-r--r--sbin/dhclient/alloc.c76
-rw-r--r--sbin/dhclient/bpf.c374
-rw-r--r--sbin/dhclient/clparse.c938
-rw-r--r--sbin/dhclient/conflex.c525
-rw-r--r--sbin/dhclient/convert.c114
-rw-r--r--sbin/dhclient/dhclient-script227
-rw-r--r--sbin/dhclient/dhclient-script.8246
-rw-r--r--sbin/dhclient/dhclient.8181
-rw-r--r--sbin/dhclient/dhclient.c2373
-rw-r--r--sbin/dhclient/dhclient.conf36
-rw-r--r--sbin/dhclient/dhclient.conf.5541
-rw-r--r--sbin/dhclient/dhclient.leases.587
-rw-r--r--sbin/dhclient/dhcp-options.5590
-rw-r--r--sbin/dhclient/dhcp.h168
-rw-r--r--sbin/dhclient/dhcpd.h437
-rw-r--r--sbin/dhclient/dhctoken.h136
-rw-r--r--sbin/dhclient/dispatch.c495
-rw-r--r--sbin/dhclient/errwarn.c234
-rw-r--r--sbin/dhclient/hash.c119
-rw-r--r--sbin/dhclient/inet.c118
-rw-r--r--sbin/dhclient/options.c717
-rw-r--r--sbin/dhclient/packet.c253
-rw-r--r--sbin/dhclient/parse.c577
-rw-r--r--sbin/dhclient/privsep.c235
-rw-r--r--sbin/dhclient/privsep.h47
-rw-r--r--sbin/dhclient/tables.c430
-rw-r--r--sbin/dhclient/tree.c56
-rw-r--r--sbin/dhclient/tree.h66
29 files changed, 10434 insertions, 20 deletions
diff --git a/sbin/dhclient/Makefile b/sbin/dhclient/Makefile
index 3148ea8f0bc5..594c9aff1b4e 100644
--- a/sbin/dhclient/Makefile
+++ b/sbin/dhclient/Makefile
@@ -1,30 +1,48 @@
-# ex:ts=8
+# $OpenBSD: Makefile,v 1.9 2004/05/04 12:52:05 henning Exp $
#
-# @(#)Makefile 1.0 (obrien) 2/6/99
-# $Id$
+# Copyright (c) 1996, 1997 The Internet Software Consortium.
+# All rights reserved.
#
-# Copyright (c) 1999 by David O'Brien
-# This file is under a "FreeBSD" copyright. See /usr/src/sys/sys/copyright.h
-# for the terms of the copyright.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of The Internet Software Consortium nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE INTERNET SOFTWARE CONSORTIUM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
#
-.PATH: ${.CURDIR}/../../contrib/isc-dhcp/client ${.CURDIR}/../../contrib/isc-dhcp/common
+.include <bsd.own.mk>
-PROG= dhclient
-MAN5= dhclient.conf.5 dhclient.leases.5 dhcp-options.5
-MAN8= dhclient.8 dhclient-script.8
+SRCS= dhclient.c clparse.c alloc.c dispatch.c hash.c bpf.c options.c \
+ tree.c conflex.c errwarn.c inet.c packet.c convert.c tables.c \
+ parse.c privsep.c
-SRCS= dhclient.c clparse.c
-SRCS+= raw.c parse.c nit.c icmp.c dispatch.c conflex.c upf.c bpf.c \
- socket.c packet.c memory.c print.c options.c inet.c convert.c \
- tree.c tables.c hash.c alloc.c errwarn.c inet_addr.c
+PROG= dhclient
+MAN= dhclient.8 dhclient.conf.5 dhclient.leases.5 dhclient-script.8
-CFLAGS+= -I${.CURDIR}/../../contrib/isc-dhcp/includes \
- -I${.CURDIR}/../../contrib/isc-dhcp
+CFLAGS+=-Wall
-afterinstall:
- ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
- ${.CURDIR}/../../contrib/isc-dhcp/client/scripts/freebsd \
- ${BINDIR}/dhclient-script
+beforeinstall:
+ ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/dhclient-script ${DESTDIR}/sbin/dhclient-script
.include <bsd.prog.mk>
diff --git a/sbin/dhclient/alloc.c b/sbin/dhclient/alloc.c
new file mode 100644
index 000000000000..cabb76d42989
--- /dev/null
+++ b/sbin/dhclient/alloc.c
@@ -0,0 +1,76 @@
+/* $OpenBSD: alloc.c,v 1.9 2004/05/04 20:28:40 deraadt Exp $ */
+
+/* Memory allocation... */
+
+/*
+ * Copyright (c) 1995, 1996, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+struct string_list *
+new_string_list(size_t size)
+{
+ struct string_list *rval;
+
+ rval = calloc(1, sizeof(struct string_list) + size);
+ if (rval != NULL)
+ rval->string = ((char *)rval) + sizeof(struct string_list);
+ return (rval);
+}
+
+struct hash_table *
+new_hash_table(int count)
+{
+ struct hash_table *rval;
+
+ rval = calloc(1, sizeof(struct hash_table) -
+ (DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *)) +
+ (count * sizeof(struct hash_bucket *)));
+ if (rval == NULL)
+ return (NULL);
+ rval->hash_count = count;
+ return (rval);
+}
+
+struct hash_bucket *
+new_hash_bucket(void)
+{
+ struct hash_bucket *rval = calloc(1, sizeof(struct hash_bucket));
+
+ return (rval);
+}
diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c
new file mode 100644
index 000000000000..131a88f743d2
--- /dev/null
+++ b/sbin/dhclient/bpf.c
@@ -0,0 +1,374 @@
+/* $OpenBSD: bpf.c,v 1.13 2004/05/05 14:28:58 deraadt Exp $ */
+
+/* BPF socket interface code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <net/bpf.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define BPF_FORMAT "/dev/bpf%d"
+
+/*
+ * Called by get_interface_list for each interface that's discovered.
+ * Opens a packet filter for each interface and adds it to the select
+ * mask.
+ */
+int
+if_register_bpf(struct interface_info *info)
+{
+ char filename[50];
+ int sock, b;
+
+ /* Open a BPF device */
+ for (b = 0; 1; b++) {
+ snprintf(filename, sizeof(filename), BPF_FORMAT, b);
+ sock = open(filename, O_RDWR, 0);
+ if (sock < 0) {
+ if (errno == EBUSY)
+ continue;
+ else
+ error("Can't find free bpf: %m");
+ } else
+ break;
+ }
+
+ /* Set the BPF device to point at this interface. */
+ if (ioctl(sock, BIOCSETIF, info->ifp) < 0)
+ error("Can't attach interface %s to bpf device %s: %m",
+ info->name, filename);
+
+ return (sock);
+}
+
+void
+if_register_send(struct interface_info *info)
+{
+ /*
+ * If we're using the bpf API for sending and receiving, we
+ * don't need to register this interface twice.
+ */
+ info->wfdesc = info->rfdesc;
+}
+
+/*
+ * Packet filter program...
+ *
+ * XXX: Changes to the filter program may require changes to the
+ * constant offsets used in if_register_send to patch the BPF program!
+ */
+struct bpf_insn dhcp_bpf_filter[] = {
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's to the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
+
+/*
+ * Packet write filter program:
+ * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
+ */
+struct bpf_insn dhcp_bpf_wfilter[] = {
+ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
+
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's from the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3),
+
+ /* Make sure it is to the right ports ... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
+
+void
+if_register_receive(struct interface_info *info)
+{
+ struct bpf_version v;
+ struct bpf_program p;
+ int flag = 1, sz;
+
+ /* Open a BPF device and hang it on this interface... */
+ info->rfdesc = if_register_bpf(info);
+
+ /* Make sure the BPF version is in range... */
+ if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0)
+ error("Can't get BPF version: %m");
+
+ if (v.bv_major != BPF_MAJOR_VERSION ||
+ v.bv_minor < BPF_MINOR_VERSION)
+ error("Kernel BPF version out of range - recompile dhcpd!");
+
+ /*
+ * Set immediate mode so that reads return as soon as a packet
+ * comes in, rather than waiting for the input buffer to fill
+ * with packets.
+ */
+ if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) < 0)
+ error("Can't set immediate mode on bpf device: %m");
+
+ /* Get the required BPF buffer length from the kernel. */
+ if (ioctl(info->rfdesc, BIOCGBLEN, &sz) < 0)
+ error("Can't get bpf buffer length: %m");
+ info->rbuf_max = sz;
+ info->rbuf = malloc(info->rbuf_max);
+ if (!info->rbuf)
+ error("Can't allocate %lu bytes for bpf input buffer.",
+ (unsigned long)info->rbuf_max);
+ info->rbuf_offset = 0;
+ info->rbuf_len = 0;
+
+ /* Set up the bpf filter program structure. */
+ p.bf_len = dhcp_bpf_filter_len;
+ p.bf_insns = dhcp_bpf_filter;
+
+ /* Patch the server port into the BPF program...
+ *
+ * XXX: changes to filter program may require changes to the
+ * insn number(s) used below!
+ */
+ dhcp_bpf_filter[8].k = LOCAL_PORT;
+
+ if (ioctl(info->rfdesc, BIOCSETF, &p) < 0)
+ error("Can't install packet filter program: %m");
+
+ /* Set up the bpf write filter program structure. */
+ p.bf_len = dhcp_bpf_wfilter_len;
+ p.bf_insns = dhcp_bpf_wfilter;
+
+ if (dhcp_bpf_wfilter[7].k == 0x1fff)
+ dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK);
+
+ if (ioctl(info->rfdesc, BIOCSETWF, &p) < 0)
+ error("Can't install write filter program: %m");
+
+ if (ioctl(info->rfdesc, BIOCLOCK, NULL) < 0)
+ error("Cannot lock bpf");
+}
+
+ssize_t
+send_packet(struct interface_info *interface, struct dhcp_packet *raw,
+ size_t len, struct in_addr from, struct sockaddr_in *to,
+ struct hardware *hto)
+{
+ unsigned char buf[256];
+ struct iovec iov[2];
+ int result, bufp = 0;
+
+ /* Assemble the headers... */
+ assemble_hw_header(interface, buf, &bufp, hto);
+ assemble_udp_ip_header(buf, &bufp, from.s_addr,
+ to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
+
+ /* Fire it off */
+ iov[0].iov_base = (char *)buf;
+ iov[0].iov_len = bufp;
+ iov[1].iov_base = (char *)raw;
+ iov[1].iov_len = len;
+
+ result = writev(interface->wfdesc, iov, 2);
+ if (result < 0)
+ warning("send_packet: %m");
+ return (result);
+}
+
+ssize_t
+receive_packet(struct interface_info *interface, unsigned char *buf,
+ size_t len, struct sockaddr_in *from, struct hardware *hfrom)
+{
+ int length = 0, offset = 0;
+ struct bpf_hdr hdr;
+
+ /*
+ * All this complexity is because BPF doesn't guarantee that
+ * only one packet will be returned at a time. We're getting
+ * what we deserve, though - this is a terrible abuse of the BPF
+ * interface. Sigh.
+ */
+
+ /* Process packets until we get one we can return or until we've
+ * done a read and gotten nothing we can return...
+ */
+ do {
+ /* If the buffer is empty, fill it. */
+ if (interface->rbuf_offset == interface->rbuf_len) {
+ length = read(interface->rfdesc, interface->rbuf,
+ interface->rbuf_max);
+ if (length <= 0)
+ return (length);
+ interface->rbuf_offset = 0;
+ interface->rbuf_len = length;
+ }
+
+ /*
+ * If there isn't room for a whole bpf header, something
+ * went wrong, but we'll ignore it and hope it goes
+ * away... XXX
+ */
+ if (interface->rbuf_len - interface->rbuf_offset <
+ sizeof(hdr)) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /* Copy out a bpf header... */
+ memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
+ sizeof(hdr));
+
+ /*
+ * If the bpf header plus data doesn't fit in what's
+ * left of the buffer, stick head in sand yet again...
+ */
+ if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
+ interface->rbuf_len) {
+ interface->rbuf_offset = interface->rbuf_len;
+ continue;
+ }
+
+ /*
+ * If the captured data wasn't the whole packet, or if
+ * the packet won't fit in the input buffer, all we can
+ * do is drop it.
+ */
+ if (hdr.bh_caplen != hdr.bh_datalen) {
+ interface->rbuf_offset += hdr.bh_hdrlen = hdr.bh_caplen;
+ continue;
+ }
+
+ /* Skip over the BPF header... */
+ interface->rbuf_offset += hdr.bh_hdrlen;
+
+ /* Decode the physical header... */
+ offset = decode_hw_header(interface->rbuf,
+ interface->rbuf_offset, hfrom);
+
+ /*
+ * If a physical layer checksum failed (dunno of any
+ * physical layer that supports this, but WTH), skip
+ * this packet.
+ */
+ if (offset < 0) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header(interface->rbuf,
+ interface->rbuf_offset, from, NULL, hdr.bh_caplen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+ interface->rbuf_offset += offset;
+ hdr.bh_caplen -= offset;
+
+ /*
+ * If there's not enough room to stash the packet data,
+ * we have to skip it (this shouldn't happen in real
+ * life, though).
+ */
+ if (hdr.bh_caplen > len) {
+ interface->rbuf_offset += hdr.bh_caplen;
+ continue;
+ }
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, interface->rbuf + interface->rbuf_offset,
+ hdr.bh_caplen);
+ interface->rbuf_offset += hdr.bh_caplen;
+ return (hdr.bh_caplen);
+ } while (!length);
+ return (0);
+}
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c
new file mode 100644
index 000000000000..2777902c7767
--- /dev/null
+++ b/sbin/dhclient/clparse.c
@@ -0,0 +1,938 @@
+/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
+
+/* Parser for dhclient config and lease files... */
+
+/*
+ * Copyright (c) 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+struct client_config top_level_config;
+struct interface_info *dummy_interfaces;
+extern struct interface_info *ifi;
+
+char client_script_name[] = "/sbin/dhclient-script";
+
+/*
+ * client-conf-file :== client-declarations EOF
+ * client-declarations :== <nil>
+ * | client-declaration
+ * | client-declarations client-declaration
+ */
+int
+read_client_conf(void)
+{
+ FILE *cfile;
+ char *val;
+ int token;
+ struct client_config *config;
+
+ new_parse(path_dhclient_conf);
+
+ /* Set up the initial dhcp option universe. */
+ initialize_universes();
+
+ /* Initialize the top level client configuration. */
+ memset(&top_level_config, 0, sizeof(top_level_config));
+
+ /* Set some defaults... */
+ top_level_config.timeout = 60;
+ top_level_config.select_interval = 0;
+ top_level_config.reboot_timeout = 10;
+ top_level_config.retry_interval = 300;
+ top_level_config.backoff_cutoff = 15;
+ top_level_config.initial_interval = 3;
+ top_level_config.bootp_policy = ACCEPT;
+ top_level_config.script_name = client_script_name;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_ROUTERS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] =
+ DHO_DOMAIN_NAME_SERVERS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_HOST_NAME;
+
+ if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF)
+ break;
+ parse_client_statement(cfile, NULL, &top_level_config);
+ } while (1);
+ token = next_token(&val, cfile); /* Clear the peek buffer */
+ fclose(cfile);
+ }
+
+ /*
+ * Set up state and config structures for clients that don't
+ * have per-interface configuration declarations.
+ */
+ config = NULL;
+ if (!ifi->client) {
+ ifi->client = malloc(sizeof(struct client_state));
+ if (!ifi->client)
+ error("no memory for client state.");
+ memset(ifi->client, 0, sizeof(*(ifi->client)));
+ }
+ if (!ifi->client->config) {
+ if (!config) {
+ config = malloc(sizeof(struct client_config));
+ if (!config)
+ error("no memory for client config.");
+ memcpy(config, &top_level_config,
+ sizeof(top_level_config));
+ }
+ ifi->client->config = config;
+ }
+
+ return (!warnings_occurred);
+}
+
+/*
+ * lease-file :== client-lease-statements EOF
+ * client-lease-statements :== <nil>
+ * | client-lease-statements LEASE client-lease-statement
+ */
+void
+read_client_leases(void)
+{
+ FILE *cfile;
+ char *val;
+ int token;
+
+ new_parse(path_dhclient_db);
+
+ /* Open the lease file. If we can't open it, just return -
+ we can safely trust the server to remember our state. */
+ if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
+ return;
+ do {
+ token = next_token(&val, cfile);
+ if (token == EOF)
+ break;
+ if (token != LEASE) {
+ warning("Corrupt lease file - possible data loss!");
+ skip_to_semi(cfile);
+ break;
+ } else
+ parse_client_lease_statement(cfile, 0);
+
+ } while (1);
+ fclose(cfile);
+}
+
+/*
+ * client-declaration :==
+ * SEND option-decl |
+ * DEFAULT option-decl |
+ * SUPERSEDE option-decl |
+ * PREPEND option-decl |
+ * APPEND option-decl |
+ * hardware-declaration |
+ * REQUEST option-list |
+ * REQUIRE option-list |
+ * TIMEOUT number |
+ * RETRY number |
+ * REBOOT number |
+ * SELECT_TIMEOUT number |
+ * SCRIPT string |
+ * interface-declaration |
+ * LEASE client-lease-statement |
+ * ALIAS client-lease-statement
+ */
+void
+parse_client_statement(FILE *cfile, struct interface_info *ip,
+ struct client_config *config)
+{
+ int token;
+ char *val;
+ struct option *option;
+
+ switch (next_token(&val, cfile)) {
+ case SEND:
+ parse_option_decl(cfile, &config->send_options[0]);
+ return;
+ case DEFAULT:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_DEFAULT;
+ return;
+ case SUPERSEDE:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] =
+ ACTION_SUPERSEDE;
+ return;
+ case APPEND:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_APPEND;
+ return;
+ case PREPEND:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_PREPEND;
+ return;
+ case MEDIA:
+ parse_string_list(cfile, &config->media, 1);
+ return;
+ case HARDWARE:
+ if (ip)
+ parse_hardware_param(cfile, &ip->hw_address);
+ else {
+ parse_warn("hardware address parameter %s",
+ "not allowed here.");
+ skip_to_semi(cfile);
+ }
+ return;
+ case REQUEST:
+ config->requested_option_count =
+ parse_option_list(cfile, config->requested_options);
+ return;
+ case REQUIRE:
+ memset(config->required_options, 0,
+ sizeof(config->required_options));
+ parse_option_list(cfile, config->required_options);
+ return;
+ case TIMEOUT:
+ parse_lease_time(cfile, &config->timeout);
+ return;
+ case RETRY:
+ parse_lease_time(cfile, &config->retry_interval);
+ return;
+ case SELECT_TIMEOUT:
+ parse_lease_time(cfile, &config->select_interval);
+ return;
+ case REBOOT:
+ parse_lease_time(cfile, &config->reboot_timeout);
+ return;
+ case BACKOFF_CUTOFF:
+ parse_lease_time(cfile, &config->backoff_cutoff);
+ return;
+ case INITIAL_INTERVAL:
+ parse_lease_time(cfile, &config->initial_interval);
+ return;
+ case SCRIPT:
+ config->script_name = parse_string(cfile);
+ return;
+ case INTERFACE:
+ if (ip)
+ parse_warn("nested interface declaration.");
+ parse_interface_declaration(cfile, config);
+ return;
+ case LEASE:
+ parse_client_lease_statement(cfile, 1);
+ return;
+ case ALIAS:
+ parse_client_lease_statement(cfile, 2);
+ return;
+ case REJECT:
+ parse_reject_statement(cfile, config);
+ return;
+ default:
+ parse_warn("expecting a statement.");
+ skip_to_semi(cfile);
+ break;
+ }
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ }
+}
+
+int
+parse_X(FILE *cfile, u_int8_t *buf, int max)
+{
+ int token;
+ char *val;
+ int len;
+
+ token = peek_token(&val, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ len = 0;
+ do {
+ token = next_token(&val, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn("expecting hexadecimal constant.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ convert_num(&buf[len], val, 16, 8);
+ if (len++ > max) {
+ parse_warn("hexadecimal constant too long.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ token = peek_token(&val, cfile);
+ if (token == COLON)
+ token = next_token(&val, cfile);
+ } while (token == COLON);
+ val = (char *)buf;
+ } else if (token == STRING) {
+ token = next_token(&val, cfile);
+ len = strlen(val);
+ if (len + 1 > max) {
+ parse_warn("string constant too long.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ memcpy(buf, val, len + 1);
+ } else {
+ parse_warn("expecting string or hexadecimal data");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (len);
+}
+
+/*
+ * option-list :== option_name |
+ * option_list COMMA option_name
+ */
+int
+parse_option_list(FILE *cfile, u_int8_t *list)
+{
+ int ix, i;
+ int token;
+ char *val;
+
+ ix = 0;
+ do {
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expected option name.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ for (i = 0; i < 256; i++)
+ if (!strcasecmp(dhcp_options[i].name, val))
+ break;
+
+ if (i == 256) {
+ parse_warn("%s: unexpected option name.", val);
+ skip_to_semi(cfile);
+ return (0);
+ }
+ list[ix++] = i;
+ if (ix == 256) {
+ parse_warn("%s: too many options.", val);
+ skip_to_semi(cfile);
+ return (0);
+ }
+ token = next_token(&val, cfile);
+ } while (token == COMMA);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (ix);
+}
+
+/*
+ * interface-declaration :==
+ * INTERFACE string LBRACE client-declarations RBRACE
+ */
+void
+parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
+{
+ int token;
+ char *val;
+ struct interface_info *ip;
+
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting interface name (in quotes).");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ ip = interface_or_dummy(val);
+
+ if (!ip->client)
+ make_client_state(ip);
+
+ if (!ip->client->config)
+ make_client_config(ip, outer_config);
+
+ token = next_token(&val, cfile);
+ if (token != LBRACE) {
+ parse_warn("expecting left brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF) {
+ parse_warn("unterminated interface declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_statement(cfile, ip, ip->client->config);
+ } while (1);
+ token = next_token(&val, cfile);
+}
+
+struct interface_info *
+interface_or_dummy(char *name)
+{
+ struct interface_info *ip;
+
+ /* Find the interface (if any) that matches the name. */
+ if (!strcmp(ifi->name, name))
+ return (ifi);
+
+ /* If it's not a real interface, see if it's on the dummy list. */
+ for (ip = dummy_interfaces; ip; ip = ip->next)
+ if (!strcmp(ip->name, name))
+ return (ip);
+
+ /*
+ * If we didn't find an interface, make a dummy interface as a
+ * placeholder.
+ */
+ ip = malloc(sizeof(*ip));
+ if (!ip)
+ error("Insufficient memory to record interface %s", name);
+ memset(ip, 0, sizeof(*ip));
+ strlcpy(ip->name, name, IFNAMSIZ);
+ ip->next = dummy_interfaces;
+ dummy_interfaces = ip;
+ return (ip);
+}
+
+void
+make_client_state(struct interface_info *ip)
+{
+ ip->client = malloc(sizeof(*(ip->client)));
+ if (!ip->client)
+ error("no memory for state on %s", ip->name);
+ memset(ip->client, 0, sizeof(*(ip->client)));
+}
+
+void
+make_client_config(struct interface_info *ip, struct client_config *config)
+{
+ ip->client->config = malloc(sizeof(struct client_config));
+ if (!ip->client->config)
+ error("no memory for config for %s", ip->name);
+ memset(ip->client->config, 0, sizeof(*(ip->client->config)));
+ memcpy(ip->client->config, config, sizeof(*config));
+}
+
+/*
+ * client-lease-statement :==
+ * RBRACE client-lease-declarations LBRACE
+ *
+ * client-lease-declarations :==
+ * <nil> |
+ * client-lease-declaration |
+ * client-lease-declarations client-lease-declaration
+ */
+void
+parse_client_lease_statement(FILE *cfile, int is_static)
+{
+ struct client_lease *lease, *lp, *pl;
+ struct interface_info *ip;
+ int token;
+ char *val;
+
+ token = next_token(&val, cfile);
+ if (token != LBRACE) {
+ parse_warn("expecting left brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ lease = malloc(sizeof(struct client_lease));
+ if (!lease)
+ error("no memory for lease.");
+ memset(lease, 0, sizeof(*lease));
+ lease->is_static = is_static;
+
+ ip = NULL;
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF) {
+ parse_warn("unterminated lease declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_lease_declaration(cfile, lease, &ip);
+ } while (1);
+ token = next_token(&val, cfile);
+
+ /* If the lease declaration didn't include an interface
+ * declaration that we recognized, it's of no use to us.
+ */
+ if (!ip) {
+ free_client_lease(lease);
+ return;
+ }
+
+ /* Make sure there's a client state structure... */
+ if (!ip->client)
+ make_client_state(ip);
+
+ /* If this is an alias lease, it doesn't need to be sorted in. */
+ if (is_static == 2) {
+ ip->client->alias = lease;
+ return;
+ }
+
+ /*
+ * The new lease may supersede a lease that's not the active
+ * lease but is still on the lease list, so scan the lease list
+ * looking for a lease with the same address, and if we find it,
+ * toss it.
+ */
+ pl = NULL;
+ for (lp = ip->client->leases; lp; lp = lp->next) {
+ if (lp->address.len == lease->address.len &&
+ !memcmp(lp->address.iabuf, lease->address.iabuf,
+ lease->address.len)) {
+ if (pl)
+ pl->next = lp->next;
+ else
+ ip->client->leases = lp->next;
+ free_client_lease(lp);
+ break;
+ }
+ }
+
+ /*
+ * If this is a preloaded lease, just put it on the list of
+ * recorded leases - don't make it the active lease.
+ */
+ if (is_static) {
+ lease->next = ip->client->leases;
+ ip->client->leases = lease;
+ return;
+ }
+
+ /*
+ * The last lease in the lease file on a particular interface is
+ * the active lease for that interface. Of course, we don't
+ * know what the last lease in the file is until we've parsed
+ * the whole file, so at this point, we assume that the lease we
+ * just parsed is the active lease for its interface. If
+ * there's already an active lease for the interface, and this
+ * lease is for the same ip address, then we just toss the old
+ * active lease and replace it with this one. If this lease is
+ * for a different address, then if the old active lease has
+ * expired, we dump it; if not, we put it on the list of leases
+ * for this interface which are still valid but no longer
+ * active.
+ */
+ if (ip->client->active) {
+ if (ip->client->active->expiry < cur_time)
+ free_client_lease(ip->client->active);
+ else if (ip->client->active->address.len ==
+ lease->address.len &&
+ !memcmp(ip->client->active->address.iabuf,
+ lease->address.iabuf, lease->address.len))
+ free_client_lease(ip->client->active);
+ else {
+ ip->client->active->next = ip->client->leases;
+ ip->client->leases = ip->client->active;
+ }
+ }
+ ip->client->active = lease;
+
+ /* Phew. */
+}
+
+/*
+ * client-lease-declaration :==
+ * BOOTP |
+ * INTERFACE string |
+ * FIXED_ADDR ip_address |
+ * FILENAME string |
+ * SERVER_NAME string |
+ * OPTION option-decl |
+ * RENEW time-decl |
+ * REBIND time-decl |
+ * EXPIRE time-decl
+ */
+void
+parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
+ struct interface_info **ipp)
+{
+ int token;
+ char *val;
+ struct interface_info *ip;
+
+ switch (next_token(&val, cfile)) {
+ case BOOTP:
+ lease->is_bootp = 1;
+ break;
+ case INTERFACE:
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting interface name (in quotes).");
+ skip_to_semi(cfile);
+ break;
+ }
+ ip = interface_or_dummy(val);
+ *ipp = ip;
+ break;
+ case FIXED_ADDR:
+ if (!parse_ip_addr(cfile, &lease->address))
+ return;
+ break;
+ case MEDIUM:
+ parse_string_list(cfile, &lease->medium, 0);
+ return;
+ case FILENAME:
+ lease->filename = parse_string(cfile);
+ return;
+ case SERVER_NAME:
+ lease->server_name = parse_string(cfile);
+ return;
+ case RENEW:
+ lease->renewal = parse_date(cfile);
+ return;
+ case REBIND:
+ lease->rebind = parse_date(cfile);
+ return;
+ case EXPIRE:
+ lease->expiry = parse_date(cfile);
+ return;
+ case OPTION:
+ parse_option_decl(cfile, lease->options);
+ return;
+ default:
+ parse_warn("expecting lease declaration.");
+ skip_to_semi(cfile);
+ break;
+ }
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+struct option *
+parse_option_decl(FILE *cfile, struct option_data *options)
+{
+ char *val;
+ int token;
+ u_int8_t buf[4];
+ u_int8_t hunkbuf[1024];
+ int hunkix = 0;
+ char *vendor;
+ char *fmt;
+ struct universe *universe;
+ struct option *option;
+ struct iaddr ip_addr;
+ u_int8_t *dp;
+ int len;
+ int nul_term = 0;
+
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ if ((vendor = strdup(val)) == NULL)
+ error("no memory for vendor information.");
+
+ token = peek_token(&val, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ token = next_token(&val, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier after '.'");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Look up the option name hash table for the specified
+ vendor. */
+ universe = ((struct universe *)hash_lookup(&universe_hash,
+ (unsigned char *)vendor, 0));
+ /* If it's not there, we can't parse the rest of the
+ declaration. */
+ if (!universe) {
+ parse_warn("no vendor named %s.", vendor);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ } else {
+ /* Use the default hash table, which contains all the
+ standard dhcp option names. */
+ val = vendor;
+ universe = &dhcp_universe;
+ }
+
+ /* Look up the actual option info... */
+ option = (struct option *)hash_lookup(universe->hash,
+ (unsigned char *)val, 0);
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (!option) {
+ if (val == vendor)
+ parse_warn("no option named %s", val);
+ else
+ parse_warn("no option named %s for vendor %s",
+ val, vendor);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Free the initial identifier token. */
+ free(vendor);
+
+ /* Parse the option data... */
+ do {
+ for (fmt = option->format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ switch (*fmt) {
+ case 'X':
+ len = parse_X(cfile, &hunkbuf[hunkix],
+ sizeof(hunkbuf) - hunkix);
+ hunkix += len;
+ break;
+ case 't': /* Text string... */
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting string.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ len = strlen(val);
+ if (hunkix + len + 1 > sizeof(hunkbuf)) {
+ parse_warn("option data buffer %s",
+ "overflow");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ memcpy(&hunkbuf[hunkix], val, len + 1);
+ nul_term = 1;
+ hunkix += len;
+ break;
+ case 'I': /* IP address. */
+ if (!parse_ip_addr(cfile, &ip_addr))
+ return (NULL);
+ len = ip_addr.len;
+ dp = ip_addr.iabuf;
+alloc:
+ if (hunkix + len > sizeof(hunkbuf)) {
+ parse_warn("option data buffer "
+ "overflow");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ memcpy(&hunkbuf[hunkix], dp, len);
+ hunkix += len;
+ break;
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+need_number:
+ parse_warn("expecting number.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ convert_num(buf, val, 0, 32);
+ len = 4;
+ dp = buf;
+ goto alloc;
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token(&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num(buf, val, 0, 16);
+ len = 2;
+ dp = buf;
+ goto alloc;
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token(&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num(buf, val, 0, 8);
+ len = 1;
+ dp = buf;
+ goto alloc;
+ case 'f': /* Boolean flag. */
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier.");
+bad_flag:
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ if (!strcasecmp(val, "true") ||
+ !strcasecmp(val, "on"))
+ buf[0] = 1;
+ else if (!strcasecmp(val, "false") ||
+ !strcasecmp(val, "off"))
+ buf[0] = 0;
+ else {
+ parse_warn("expecting boolean.");
+ goto bad_flag;
+ }
+ len = 1;
+ dp = buf;
+ goto alloc;
+ default:
+ warning("Bad format %c in parse_option_param.",
+ *fmt);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ }
+ token = next_token(&val, cfile);
+ } while (*fmt == 'A' && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ options[option->code].data = malloc(hunkix + nul_term);
+ if (!options[option->code].data)
+ error("out of memory allocating option data.");
+ memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
+ options[option->code].len = hunkix;
+ return (option);
+}
+
+void
+parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
+{
+ int token;
+ char *val;
+ struct string_list *cur, *tmp;
+
+ /* Find the last medium in the media list. */
+ if (*lp)
+ for (cur = *lp; cur->next; cur = cur->next)
+ ; /* nothing */
+ else
+ cur = NULL;
+
+ do {
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("Expecting media options.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ tmp = new_string_list(strlen(val) + 1);
+ if (tmp == NULL)
+ error("no memory for string list entry.");
+ strlcpy(tmp->string, val, strlen(val) + 1);
+ tmp->next = NULL;
+
+ /* Store this medium at the end of the media list. */
+ if (cur)
+ cur->next = tmp;
+ else
+ *lp = tmp;
+ cur = tmp;
+
+ token = next_token(&val, cfile);
+ } while (multiple && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+void
+parse_reject_statement(FILE *cfile, struct client_config *config)
+{
+ int token;
+ char *val;
+ struct iaddr addr;
+ struct iaddrlist *list;
+
+ do {
+ if (!parse_ip_addr(cfile, &addr)) {
+ parse_warn("expecting IP address.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ list = malloc(sizeof(struct iaddrlist));
+ if (!list)
+ error("no memory for reject list!");
+
+ list->addr = addr;
+ list->next = config->reject_list;
+ config->reject_list = list;
+
+ token = next_token(&val, cfile);
+ } while (token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
diff --git a/sbin/dhclient/conflex.c b/sbin/dhclient/conflex.c
new file mode 100644
index 000000000000..ee96bfeb9ca0
--- /dev/null
+++ b/sbin/dhclient/conflex.c
@@ -0,0 +1,525 @@
+/* $OpenBSD: conflex.c,v 1.7 2004/09/15 19:02:38 deraadt Exp $ */
+
+/* Lexical scanner for dhcpd config file... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <ctype.h>
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+int lexline;
+int lexchar;
+char *token_line;
+char *prev_line;
+char *cur_line;
+char *tlname;
+int eol_token;
+
+static char line1[81];
+static char line2[81];
+static int lpos;
+static int line;
+static int tlpos;
+static int tline;
+static int token;
+static int ugflag;
+static char *tval;
+static char tokbuf[1500];
+
+static int get_char(FILE *);
+static int get_token(FILE *);
+static void skip_to_eol(FILE *);
+static int read_string(FILE *);
+static int read_number(int, FILE *);
+static int read_num_or_name(int, FILE *);
+static int intern(char *, int);
+
+void
+new_parse(char *name)
+{
+ tlname = name;
+ lpos = line = 1;
+ cur_line = line1;
+ prev_line = line2;
+ token_line = cur_line;
+ cur_line[0] = prev_line[0] = 0;
+ warnings_occurred = 0;
+}
+
+static int
+get_char(FILE *cfile)
+{
+ int c = getc(cfile);
+ if (!ugflag) {
+ if (c == '\n') {
+ if (cur_line == line1) {
+ cur_line = line2;
+ prev_line = line1;
+ } else {
+ cur_line = line2;
+ prev_line = line1;
+ }
+ line++;
+ lpos = 1;
+ cur_line[0] = 0;
+ } else if (c != EOF) {
+ if (lpos <= 81) {
+ cur_line[lpos - 1] = c;
+ cur_line[lpos] = 0;
+ }
+ lpos++;
+ }
+ } else
+ ugflag = 0;
+ return (c);
+}
+
+static int
+get_token(FILE *cfile)
+{
+ int c, ttok;
+ static char tb[2];
+ int l, p;
+
+ do {
+ l = line;
+ p = lpos;
+
+ c = get_char(cfile);
+
+ if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
+ continue;
+ if (c == '#') {
+ skip_to_eol(cfile);
+ continue;
+ }
+ if (c == '"') {
+ lexline = l;
+ lexchar = p;
+ ttok = read_string(cfile);
+ break;
+ }
+ if ((isascii(c) && isdigit(c)) || c == '-') {
+ lexline = l;
+ lexchar = p;
+ ttok = read_number(c, cfile);
+ break;
+ } else if (isascii(c) && isalpha(c)) {
+ lexline = l;
+ lexchar = p;
+ ttok = read_num_or_name(c, cfile);
+ break;
+ } else {
+ lexline = l;
+ lexchar = p;
+ tb[0] = c;
+ tb[1] = 0;
+ tval = tb;
+ ttok = c;
+ break;
+ }
+ } while (1);
+ return (ttok);
+}
+
+int
+next_token(char **rval, FILE *cfile)
+{
+ int rv;
+
+ if (token) {
+ if (lexline != tline)
+ token_line = cur_line;
+ lexchar = tlpos;
+ lexline = tline;
+ rv = token;
+ token = 0;
+ } else {
+ rv = get_token(cfile);
+ token_line = cur_line;
+ }
+ if (rval)
+ *rval = tval;
+
+ return (rv);
+}
+
+int
+peek_token(char **rval, FILE *cfile)
+{
+ int x;
+
+ if (!token) {
+ tlpos = lexchar;
+ tline = lexline;
+ token = get_token(cfile);
+ if (lexline != tline)
+ token_line = prev_line;
+ x = lexchar;
+ lexchar = tlpos;
+ tlpos = x;
+ x = lexline;
+ lexline = tline;
+ tline = x;
+ }
+ if (rval)
+ *rval = tval;
+
+ return (token);
+}
+
+static void
+skip_to_eol(FILE *cfile)
+{
+ int c;
+
+ do {
+ c = get_char(cfile);
+ if (c == EOF)
+ return;
+ if (c == '\n')
+ return;
+ } while (1);
+}
+
+static int
+read_string(FILE *cfile)
+{
+ int i, c, bs = 0;
+
+ for (i = 0; i < sizeof(tokbuf); i++) {
+ c = get_char(cfile);
+ if (c == EOF) {
+ parse_warn("eof in string constant");
+ break;
+ }
+ if (bs) {
+ bs = 0;
+ tokbuf[i] = c;
+ } else if (c == '\\')
+ bs = 1;
+ else if (c == '"')
+ break;
+ else
+ tokbuf[i] = c;
+ }
+ /*
+ * Normally, I'd feel guilty about this, but we're talking about
+ * strings that'll fit in a DHCP packet here...
+ */
+ if (i == sizeof(tokbuf)) {
+ parse_warn("string constant larger than internal buffer");
+ i--;
+ }
+ tokbuf[i] = 0;
+ tval = tokbuf;
+ return (STRING);
+}
+
+static int
+read_number(int c, FILE *cfile)
+{
+ int seenx = 0, i = 0, token = NUMBER;
+
+ tokbuf[i++] = c;
+ for (; i < sizeof(tokbuf); i++) {
+ c = get_char(cfile);
+ if (!seenx && c == 'x')
+ seenx = 1;
+ else if (!isascii(c) || !isxdigit(c)) {
+ ungetc(c, cfile);
+ ugflag = 1;
+ break;
+ }
+ tokbuf[i] = c;
+ }
+ if (i == sizeof(tokbuf)) {
+ parse_warn("numeric token larger than internal buffer");
+ i--;
+ }
+ tokbuf[i] = 0;
+ tval = tokbuf;
+
+ return (token);
+}
+
+static int
+read_num_or_name(int c, FILE *cfile)
+{
+ int i = 0;
+ int rv = NUMBER_OR_NAME;
+
+ tokbuf[i++] = c;
+ for (; i < sizeof(tokbuf); i++) {
+ c = get_char(cfile);
+ if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
+ ungetc(c, cfile);
+ ugflag = 1;
+ break;
+ }
+ if (!isxdigit(c))
+ rv = NAME;
+ tokbuf[i] = c;
+ }
+ if (i == sizeof(tokbuf)) {
+ parse_warn("token larger than internal buffer");
+ i--;
+ }
+ tokbuf[i] = 0;
+ tval = tokbuf;
+
+ return (intern(tval, rv));
+}
+
+static int
+intern(char *atom, int dfv)
+{
+ if (!isascii(atom[0]))
+ return (dfv);
+
+ switch (tolower(atom[0])) {
+ case 'a':
+ if (!strcasecmp(atom + 1, "lways-reply-rfc1048"))
+ return (ALWAYS_REPLY_RFC1048);
+ if (!strcasecmp(atom + 1, "ppend"))
+ return (APPEND);
+ if (!strcasecmp(atom + 1, "llow"))
+ return (ALLOW);
+ if (!strcasecmp(atom + 1, "lias"))
+ return (ALIAS);
+ if (!strcasecmp(atom + 1, "bandoned"))
+ return (ABANDONED);
+ if (!strcasecmp(atom + 1, "uthoritative"))
+ return (AUTHORITATIVE);
+ break;
+ case 'b':
+ if (!strcasecmp(atom + 1, "ackoff-cutoff"))
+ return (BACKOFF_CUTOFF);
+ if (!strcasecmp(atom + 1, "ootp"))
+ return (BOOTP);
+ if (!strcasecmp(atom + 1, "ooting"))
+ return (BOOTING);
+ if (!strcasecmp(atom + 1, "oot-unknown-clients"))
+ return (BOOT_UNKNOWN_CLIENTS);
+ case 'c':
+ if (!strcasecmp(atom + 1, "lass"))
+ return (CLASS);
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (CIADDR);
+ if (!strcasecmp(atom + 1, "lient-identifier"))
+ return (CLIENT_IDENTIFIER);
+ if (!strcasecmp(atom + 1, "lient-hostname"))
+ return (CLIENT_HOSTNAME);
+ break;
+ case 'd':
+ if (!strcasecmp(atom + 1, "omain"))
+ return (DOMAIN);
+ if (!strcasecmp(atom + 1, "eny"))
+ return (DENY);
+ if (!strncasecmp(atom + 1, "efault", 6)) {
+ if (!atom[7])
+ return (DEFAULT);
+ if (!strcasecmp(atom + 7, "-lease-time"))
+ return (DEFAULT_LEASE_TIME);
+ break;
+ }
+ if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
+ if (!atom[13])
+ return (DYNAMIC_BOOTP);
+ if (!strcasecmp(atom + 13, "-lease-cutoff"))
+ return (DYNAMIC_BOOTP_LEASE_CUTOFF);
+ if (!strcasecmp(atom + 13, "-lease-length"))
+ return (DYNAMIC_BOOTP_LEASE_LENGTH);
+ break;
+ }
+ break;
+ case 'e':
+ if (!strcasecmp(atom + 1, "thernet"))
+ return (ETHERNET);
+ if (!strcasecmp(atom + 1, "nds"))
+ return (ENDS);
+ if (!strcasecmp(atom + 1, "xpire"))
+ return (EXPIRE);
+ break;
+ case 'f':
+ if (!strcasecmp(atom + 1, "ilename"))
+ return (FILENAME);
+ if (!strcasecmp(atom + 1, "ixed-address"))
+ return (FIXED_ADDR);
+ if (!strcasecmp(atom + 1, "ddi"))
+ return (FDDI);
+ break;
+ case 'g':
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (GIADDR);
+ if (!strcasecmp(atom + 1, "roup"))
+ return (GROUP);
+ if (!strcasecmp(atom + 1, "et-lease-hostnames"))
+ return (GET_LEASE_HOSTNAMES);
+ break;
+ case 'h':
+ if (!strcasecmp(atom + 1, "ost"))
+ return (HOST);
+ if (!strcasecmp(atom + 1, "ardware"))
+ return (HARDWARE);
+ if (!strcasecmp(atom + 1, "ostname"))
+ return (HOSTNAME);
+ break;
+ case 'i':
+ if (!strcasecmp(atom + 1, "nitial-interval"))
+ return (INITIAL_INTERVAL);
+ if (!strcasecmp(atom + 1, "nterface"))
+ return (INTERFACE);
+ break;
+ case 'l':
+ if (!strcasecmp(atom + 1, "ease"))
+ return (LEASE);
+ break;
+ case 'm':
+ if (!strcasecmp(atom + 1, "ax-lease-time"))
+ return (MAX_LEASE_TIME);
+ if (!strncasecmp(atom + 1, "edi", 3)) {
+ if (!strcasecmp(atom + 4, "a"))
+ return (MEDIA);
+ if (!strcasecmp(atom + 4, "um"))
+ return (MEDIUM);
+ break;
+ }
+ break;
+ case 'n':
+ if (!strcasecmp(atom + 1, "ameserver"))
+ return (NAMESERVER);
+ if (!strcasecmp(atom + 1, "etmask"))
+ return (NETMASK);
+ if (!strcasecmp(atom + 1, "ext-server"))
+ return (NEXT_SERVER);
+ if (!strcasecmp(atom + 1, "ot"))
+ return (TOKEN_NOT);
+ break;
+ case 'o':
+ if (!strcasecmp(atom + 1, "ption"))
+ return (OPTION);
+ if (!strcasecmp(atom + 1, "ne-lease-per-client"))
+ return (ONE_LEASE_PER_CLIENT);
+ break;
+ case 'p':
+ if (!strcasecmp(atom + 1, "repend"))
+ return (PREPEND);
+ if (!strcasecmp(atom + 1, "acket"))
+ return (PACKET);
+ break;
+ case 'r':
+ if (!strcasecmp(atom + 1, "ange"))
+ return (RANGE);
+ if (!strcasecmp(atom + 1, "equest"))
+ return (REQUEST);
+ if (!strcasecmp(atom + 1, "equire"))
+ return (REQUIRE);
+ if (!strcasecmp(atom + 1, "etry"))
+ return (RETRY);
+ if (!strcasecmp(atom + 1, "enew"))
+ return (RENEW);
+ if (!strcasecmp(atom + 1, "ebind"))
+ return (REBIND);
+ if (!strcasecmp(atom + 1, "eboot"))
+ return (REBOOT);
+ if (!strcasecmp(atom + 1, "eject"))
+ return (REJECT);
+ break;
+ case 's':
+ if (!strcasecmp(atom + 1, "earch"))
+ return (SEARCH);
+ if (!strcasecmp(atom + 1, "tarts"))
+ return (STARTS);
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (SIADDR);
+ if (!strcasecmp(atom + 1, "ubnet"))
+ return (SUBNET);
+ if (!strcasecmp(atom + 1, "hared-network"))
+ return (SHARED_NETWORK);
+ if (!strcasecmp(atom + 1, "erver-name"))
+ return (SERVER_NAME);
+ if (!strcasecmp(atom + 1, "erver-identifier"))
+ return (SERVER_IDENTIFIER);
+ if (!strcasecmp(atom + 1, "elect-timeout"))
+ return (SELECT_TIMEOUT);
+ if (!strcasecmp(atom + 1, "end"))
+ return (SEND);
+ if (!strcasecmp(atom + 1, "cript"))
+ return (SCRIPT);
+ if (!strcasecmp(atom + 1, "upersede"))
+ return (SUPERSEDE);
+ break;
+ case 't':
+ if (!strcasecmp(atom + 1, "imestamp"))
+ return (TIMESTAMP);
+ if (!strcasecmp(atom + 1, "imeout"))
+ return (TIMEOUT);
+ if (!strcasecmp(atom + 1, "oken-ring"))
+ return (TOKEN_RING);
+ break;
+ case 'u':
+ if (!strncasecmp(atom + 1, "se", 2)) {
+ if (!strcasecmp(atom + 3, "r-class"))
+ return (USER_CLASS);
+ if (!strcasecmp(atom + 3, "-host-decl-names"))
+ return (USE_HOST_DECL_NAMES);
+ if (!strcasecmp(atom + 3,
+ "-lease-addr-for-default-route"))
+ return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
+ break;
+ }
+ if (!strcasecmp(atom + 1, "id"))
+ return (UID);
+ if (!strcasecmp(atom + 1, "nknown-clients"))
+ return (UNKNOWN_CLIENTS);
+ break;
+ case 'v':
+ if (!strcasecmp(atom + 1, "endor-class"))
+ return (VENDOR_CLASS);
+ break;
+ case 'y':
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return (YIADDR);
+ break;
+ }
+ return (dfv);
+}
diff --git a/sbin/dhclient/convert.c b/sbin/dhclient/convert.c
new file mode 100644
index 000000000000..0626dc4b3a8e
--- /dev/null
+++ b/sbin/dhclient/convert.c
@@ -0,0 +1,114 @@
+/* $OpenBSD: convert.c,v 1.5 2004/02/07 11:35:59 henning Exp $ */
+
+/*
+ * Safe copying of option values into and out of the option buffer,
+ * which can't be assumed to be aligned.
+ */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+u_int32_t
+getULong(unsigned char *buf)
+{
+ u_int32_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohl(ibuf));
+}
+
+int32_t
+getLong(unsigned char *(buf))
+{
+ int32_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohl(ibuf));
+}
+
+u_int16_t
+getUShort(unsigned char *buf)
+{
+ u_int16_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohs(ibuf));
+}
+
+int16_t
+getShort(unsigned char *buf)
+{
+ int16_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(ibuf));
+ return (ntohs(ibuf));
+}
+
+void
+putULong(unsigned char *obuf, u_int32_t val)
+{
+ u_int32_t tmp = htonl(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+void
+putLong(unsigned char *obuf, int32_t val)
+{
+ int32_t tmp = htonl(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+void
+putUShort(unsigned char *obuf, unsigned int val)
+{
+ u_int16_t tmp = htons(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+void
+putShort(unsigned char *obuf, int val)
+{
+ int16_t tmp = htons(val);
+
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
diff --git a/sbin/dhclient/dhclient-script b/sbin/dhclient/dhclient-script
new file mode 100644
index 000000000000..3a6ac1877f0c
--- /dev/null
+++ b/sbin/dhclient/dhclient-script
@@ -0,0 +1,227 @@
+#!/bin/sh
+#
+# $OpenBSD: dhclient-script,v 1.6 2004/05/06 18:22:41 claudio Exp $
+#
+# Copyright (c) 2003 Kenneth R Westerback <krw@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#
+
+#
+# Helper functions that implement common actions.
+#
+
+delete_old_address() {
+ if [ -n "$old_ip_address" ]; then
+ ifconfig $interface inet -alias $old_ip_address $medium
+ route delete "$old_ip_address" 127.0.0.1 >/dev/null 2>&1
+ fi
+}
+
+add_new_address() {
+ ifconfig $interface \
+ inet $new_ip_address \
+ netmask $new_subnet_mask \
+ broadcast $new_broadcast_address \
+ $medium
+
+ # XXX Original TIMEOUT code did not do this unless $new_routers was set?
+ route add $new_ip_address 127.0.0.1 >/dev/null 2>&1
+}
+
+delete_old_alias() {
+ if [ -n "$alias_ip_address" ]; then
+ ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1
+ route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1
+ fi
+}
+
+add_new_alias() {
+ if [ -n "$alias_ip_address" ]; then
+ ifconfig $interface inet alias $alias_ip_address netmask \
+ $alias_subnet_mask
+ route add $alias_ip_address 127.0.0.1
+ fi
+}
+
+delete_old_routes() {
+ # Delete existing default route. We only allow one, so no need to
+ # process $old_routers list.
+ route delete default >/dev/null 2>&1
+
+ if [ -n "$old_static_routes" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete "$1" "$2"
+ shift; shift
+ done
+ fi
+
+ arp -dan
+}
+
+add_new_routes() {
+ route delete default >/dev/null 2>&1
+ for router in $new_routers; do
+ if [ "$new_ip_address" = "$router" ]; then
+ route add default -iface $router >/dev/null 2>&1
+ else
+ route add default $router >/dev/null 2>&1
+ fi
+ # 2nd and subsequent default routers error out, so explicitly
+ # stop processing the list after the first one.
+ break
+ done
+
+ if [ -n "$new_static_routes" ]; then
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ fi
+}
+
+add_new_resolv_conf() {
+ # XXX Old code did not create/update resolv.conf unless both
+ # $new_domain_name and $new_domain_name_servers were provided. PR
+ # #3135 reported some ISP's only provide $new_domain_name_servers and
+ # thus broke the script. This code creates the resolv.conf if either
+ # are provided.
+
+ rm -f /etc/resolv.conf.std
+
+ if [ -n "$new_domain_name" ]; then
+ echo "search $new_domain_name" >>/etc/resolv.conf.std
+ fi
+
+ if [ -n "$new_domain_name_servers" ]; then
+ for nameserver in $new_domain_name_servers; do
+ echo "nameserver $nameserver" >>/etc/resolv.conf.std
+ done
+ fi
+
+ if [ -f /etc/resolv.conf.std ]; then
+ if [ -f /etc/resolv.conf.tail ]; then
+ cat /etc/resolv.conf.tail >>/etc/resolv.conf.std
+ fi
+
+ # In case (e.g. during OpenBSD installs) /etc/resolv.conf
+ # is a symbolic link, take care to preserve the link and write
+ # the new data in the correct location.
+
+ if [ -f /etc/resolv.conf ]; then
+ cat /etc/resolv.conf > /etc/resolv.conf.save
+ fi
+ cat /etc/resolv.conf.std > /etc/resolv.conf
+ rm -f /etc/resolv.conf.std
+
+ # Try to ensure correct ownership and permissions.
+ chown -RL root:wheel /etc/resolv.conf
+ chmod -RL 644 /etc/resolv.conf
+
+ return 0
+ fi
+
+ return 1
+}
+
+#
+# Start of active code.
+#
+
+if [ -n "$new_network_number" ]; then
+ echo "New Network Number: $new_network_number"
+fi
+
+if [ -n "$new_broadcast_address" ]; then
+ echo "New Broadcast Address: $new_broadcast_address"
+fi
+
+case $reason in
+MEDIUM)
+ ifconfig $interface $medium
+ ifconfig $interface inet -alias 0.0.0.0 $medium >/dev/null 2>&1
+ sleep 1
+ ;;
+
+PREINIT)
+ delete_old_alias
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 broadcast 255.255.255.255 up
+ ;;
+
+ARPCHECK|ARPSEND)
+ ;;
+
+BOUND|RENEW|REBIND|REBOOT)
+ if [ -n "$old_ip_address" ]; then
+ if [ "$old_ip_address" != "$alias_ip_address" ]; then
+ delete_old_alias
+ fi
+ if [ "$old_ip_address" != "$new_ip_address" ]; then
+ delete_old_address
+ delete_old_routes
+ fi
+ fi
+ if [ "$reason" = BOUND ] || \
+ [ "$reason" = REBOOT ] || \
+ [ -z "$old_ip_address" ] || \
+ [ "$old_ip_address" != "$new_ip_address" ]; then
+ add_new_address
+ add_new_routes
+ fi
+ if [ "$new_ip_address" != "$alias_ip_address" ]; then
+ add_new_alias
+ fi
+ add_new_resolv_conf
+ ;;
+
+EXPIRE|FAIL)
+ delete_old_alias
+ if [ -n "$old_ip_address" ]; then
+ delete_old_address
+ delete_old_routes
+ fi
+ # XXX Why add alias we just deleted above?
+ add_new_alias
+ if [ -f /etc/resolv.conf.save ]; then
+ cat /etc/resolv.conf.save > /etc/resolv.conf
+ fi
+ ;;
+
+TIMEOUT)
+ delete_old_alias
+ add_new_address
+ sleep 1
+ if [ -n "$new_routers" ]; then
+ set "$new_routers"
+ if ping -q -c 1 -w 1 "$1"; then
+ if [ "$new_ip_address" != "$alias_ip_address" ]; then
+ add_new_alias
+ fi
+ add_new_routes
+ if add_new_resolv_conf; then
+ exit 0
+ fi
+ fi
+ fi
+ ifconfig $interface inet -alias $new_ip_address $medium
+ # XXX Why not a delete_old_address as before all other invocations of
+ # delete_old_routes?
+ delete_old_routes
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/sbin/dhclient/dhclient-script.8 b/sbin/dhclient/dhclient-script.8
new file mode 100644
index 000000000000..57a2abdc54ef
--- /dev/null
+++ b/sbin/dhclient/dhclient-script.8
@@ -0,0 +1,246 @@
+.\" $OpenBSD: dhclient-script.8,v 1.2 2004/04/09 18:30:15 jmc Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1997
+.Dt DHCLIENT-SCRIPT 8
+.Os
+.Sh NAME
+.Nm dhclient-script
+.Nd DHCP client network configuration script
+.Sh DESCRIPTION
+The DHCP client network configuration script is invoked from time to
+time by
+.Xr dhclient 8 .
+This script is used by the DHCP client to set each interface's initial
+configuration prior to requesting an address, to test the address once it
+has been offered, and to set the interface's final configuration once a
+lease has been acquired.
+If no lease is acquired, the script is used to test predefined leases, if
+any, and also called once if no valid lease can be identified.
+.Pp
+.\" No standard client script exists for some operating systems, even though
+.\" the actual client may work, so a pioneering user may well need to create
+.\" a new script or modify an existing one.
+In general, customizations specific to a particular computer should be done
+in the
+.Pa /etc/dhclient.conf
+file.
+.Sh OPERATION
+When
+.Xr dhclient 8
+needs to invoke the client configuration script, it sets up a number of
+environment variables and runs
+.Nm dhclient-script .
+In all cases,
+.Va $reason
+is set to the name of the reason why the script has been invoked.
+The following reasons are currently defined:
+MEDIUM, PREINIT, ARPCHECK, ARPSEND, BOUND, RENEW, REBIND, REBOOT,
+EXPIRE, FAIL and TIMEOUT.
+.Bl -tag -width "ARPCHECK"
+.It MEDIUM
+The DHCP client is requesting that an interface's media type be set.
+The interface name is passed in
+.Va $interface ,
+and the media type is passed in
+.Va $medium .
+.It PREINIT
+The DHCP client is requesting that an interface be configured as
+required in order to send packets prior to receiving an actual address.
+.\" For clients which use the BSD socket library,
+This means configuring the interface with an IP address of 0.0.0.0
+and a broadcast address of 255.255.255.255.
+.\" For other clients, it may be possible to simply configure the interface up
+.\" without actually giving it an IP address at all.
+The interface name is passed in
+.Va $interface ,
+and the media type in
+.Va $medium .
+.Pp
+If an IP alias has been declared in
+.Xr dhclient.conf 5 ,
+its address will be passed in
+.Va $alias_ip_address ,
+and that IP alias should be deleted from the interface,
+along with any routes to it.
+.It ARPSEND
+The DHCP client is requesting that an address that has been offered to
+it be checked to see if somebody else is using it, by sending an ARP
+request for that address.
+It's not clear how to implement this, so no examples exist yet.
+The IP address to check is passed in
+.Va $new_ip_address ,
+and the interface name is passed in
+.Va $interface .
+.It ARPCHECK
+The DHCP client wants to know if a response to the ARP request sent
+using ARPSEND has been received.
+If one has, the script should exit with a nonzero status, indicating that
+the offered address has already been requested and should be declined.
+.Va $new_ip_address
+and
+.Va $interface
+are set as with ARPSEND.
+.It BOUND
+The DHCP client has done an initial binding to a new address.
+The new IP address is passed in
+.Va $new_ip_address ,
+and the interface name is passed in
+.Va $interface .
+The media type is passed in
+.Va $medium .
+Any options acquired from the server are passed using the option name
+described in
+.Xr dhcp-options 5 ,
+except that dashes
+.Pq Sq -
+are replaced by underscores
+.Pq Sq _
+in order to make valid shell variables, and the variable names start with new_.
+So for example, the new subnet mask would be passed in
+.Va $new_subnet_mask .
+.Pp
+When a binding has been completed, a lot of network parameters are
+likely to need to be set up.
+A new
+.Pa /etc/resolv.conf
+needs to be created, using the values of
+.Va $new_domain_name
+and
+.Va $new_domain_name_servers
+(which may list more than one server, separated by spaces).
+A default route should be set using
+.Va $new_routers ,
+and static routes may need to be set up using
+.Va $new_static_routes .
+.Pp
+If an IP alias has been declared, it must be set up here.
+The alias IP address will be written as
+.Va $alias_ip_address ,
+and other DHCP options that are set for the alias (e.g., subnet mask)
+will be passed in variables named as described previously except starting with
+$alias_ instead of $new_.
+Care should be taken that the alias IP address not be used if it is identical
+to the bound IP address
+.Pq Va $new_ip_address ,
+since the other alias parameters may be incorrect in this case.
+.It RENEW
+When a binding has been renewed, the script is called as in BOUND,
+except that in addition to all the variables starting with $new_,
+there is another set of variables starting with $old_.
+Persistent settings that may have changed need to be deleted \- for example,
+if a local route to the bound address is being configured, the old local
+route should be deleted.
+If the default route has changed, the old default route should be deleted.
+If the static routes have changed, the old ones should be deleted.
+Otherwise, processing can be done as with BOUND.
+.It REBIND
+The DHCP client has rebound to a new DHCP server.
+This can be handled as with RENEW, except that if the IP address has changed,
+the ARP table should be cleared.
+.It REBOOT
+The DHCP client has successfully reacquired its old address after a reboot.
+This can be processed as with BOUND.
+.It EXPIRE
+The DHCP client has failed to renew its lease or acquire a new one,
+and the lease has expired.
+The IP address must be relinquished, and all related parameters should be
+deleted, as in RENEW and REBIND.
+.It FAIL
+The DHCP client has been unable to contact any DHCP servers, and any
+leases that have been tested have not proved to be valid.
+The parameters from the last lease tested should be deconfigured.
+This can be handled in the same way as EXPIRE.
+.It TIMEOUT
+The DHCP client has been unable to contact any DHCP servers.
+However, an old lease has been identified, and its parameters have
+been passed in as with BOUND.
+The client configuration script should test these parameters and,
+if it has reason to believe they are valid, should exit with a value of zero.
+If not, it should exit with a nonzero value.
+.El
+.Pp
+The usual way to test a lease is to set up the network as with REBIND
+(since this may be called to test more than one lease) and then ping
+the first router defined in
+.Va $routers .
+If a response is received, the lease must be valid for the network to
+which the interface is currently connected.
+It would be more complete to try to ping all of the routers listed in
+.Va $new_routers ,
+as well as those listed in
+.Va $new_static_routes ,
+but current scripts do not do this.
+.\" .Sh FILES
+.\" Each operating system should generally have its own script file,
+.\" although the script files for similar operating systems may be similar
+.\" or even identical.
+.\" The script files included in the Internet Software Consortium DHCP
+.\" distribution appear in the distribution tree under client/scripts,
+.\" and bear the names of the operating systems on which they are intended
+.\" to work.
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhclient.leases 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8 ,
+.Xr dhcrelay 8
+.Sh AUTHORS
+The original version of
+.Nm
+was written for the Internet Software Consortium by
+.An Ted Lemon Aq mellon@fugue.com
+in cooperation with Vixie Enterprises.
+.Pp
+The
+.Ox
+implementation of
+.Nm
+was written by
+.An Kenneth R. Westerback Aq krw@openbsd.org .
+.Sh BUGS
+If more than one interface is being used, there's no obvious way to
+avoid clashes between server-supplied configuration parameters \- for
+example, the stock dhclient-script rewrites
+.Pa /etc/resolv.conf .
+If more than one interface is being configured,
+.Pa /etc/resolv.conf
+will be repeatedly initialized to the values provided by one server, and then
+the other.
+Assuming the information provided by both servers is valid, this shouldn't
+cause any real problems, but it could be confusing.
diff --git a/sbin/dhclient/dhclient.8 b/sbin/dhclient/dhclient.8
new file mode 100644
index 000000000000..a12952f48d8c
--- /dev/null
+++ b/sbin/dhclient/dhclient.8
@@ -0,0 +1,181 @@
+.\" $OpenBSD: dhclient.8,v 1.3 2004/04/09 18:30:15 jmc Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.Dd April 7, 2004
+.Dt DHCLIENT 8
+.Os
+.Sh NAME
+.Nm dhclient
+.Nd Dynamic Host Configuration Protocol (DHCP) Client
+.Sh SYNOPSIS
+.Nm
+.Op Fl dqu
+.Op Fl c Ar file
+.Op Fl l Ar file
+.Ar interface
+.Sh DESCRIPTION
+The
+.Nm
+utility provides a means for configuring network interfaces using DHCP, BOOTP,
+or if these protocols fail, by statically assigning an address.
+.Pp
+The name of the network interface that
+.Nm
+should attempt to
+configure must be specified on the command line.
+.Pp
+The options are as follows:
+.Bl -tag -width "-p port"
+.It Fl c Ar file
+Specify an alternate location,
+.Ar file ,
+for the configuration file.
+.It Fl d
+Forces
+.Nm
+to always run as a foreground process.
+By default,
+.Nm
+runs in the foreground until it has configured the interface, and then
+will revert to running in the background.
+.It Fl l Ar file
+Specify an alternate location,
+.Ar file ,
+for the leases file.
+.It Fl q
+Forces
+.Nm
+to be less verbose on startup.
+.It Fl u
+Forces
+.Nm
+to reject leases with unknown options in them.
+The default behaviour is to accept such lease offers.
+.El
+.Pp
+The DHCP protocol allows a host to contact a central server which
+maintains a list of IP addresses which may be assigned on one or more
+subnets.
+A DHCP client may request an address from this pool, and
+then use it on a temporary basis for communication on the network.
+The DHCP protocol also provides a mechanism whereby a client can learn
+important details about the network to which it is attached, such as
+the location of a default router, the location of a name server, and
+so on.
+.Pp
+On startup,
+.Nm
+reads
+.Pa /etc/dhclient.conf
+for configuration instructions.
+It then gets a list of all the
+network interfaces that are configured in the current system.
+It then attempts to configure each interface with DHCP.
+.Pp
+In order to keep track of leases across system reboots and server
+restarts,
+.Nm
+keeps a list of leases it has been assigned in the
+.Pa /var/db/dhclient.leases.IFNAME
+file.
+.Qq IFNAME
+represents the network interface of the DHCP client
+.Pq e.g. em0 ,
+one for each interface.
+On startup, after reading the
+.Xr dhclient.conf 5
+file,
+.Nm
+reads the leases file to refresh its memory about what leases it has been
+assigned.
+.Pp
+Old leases are kept around in case the DHCP server is unavailable when
+.Nm
+is first invoked (generally during the initial system boot
+process).
+In that event, old leases from the
+.Pa dhclient.leases.IFNAME
+file which have not yet expired are tested, and if they are determined to
+be valid, they are used until either they expire or the DHCP server
+becomes available.
+.Pp
+A mobile host which may sometimes need to access a network on which no
+DHCP server exists may be preloaded with a lease for a fixed
+address on that network.
+When all attempts to contact a DHCP server have failed,
+.Nm
+will try to validate the static lease, and if it
+succeeds, it will use that lease until it is restarted.
+.Pp
+A mobile host may also travel to some networks on which DHCP is not
+available but BOOTP is.
+In that case, it may be advantageous to
+arrange with the network administrator for an entry on the BOOTP
+database, so that the host can boot quickly on that network rather
+than cycling through the list of old leases.
+.Sh NOTES
+You must have the Berkeley Packet Filter (BPF) configured in your kernel.
+.Nm
+requires at least one
+.Pa /dev/bpf*
+file for each broadcast network interface that is attached to your system.
+See
+.Xr bpf 4
+for more information.
+.Sh FILES
+.Bl -tag -width /var/db/dhclient.leases.IFNAME~ -compact
+.It Pa /etc/dhclient.conf
+DHCP client configuration file
+.It Pa /var/db/dhclient.leases.IFNAME
+database of acquired leases
+.El
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhclient.leases 5 ,
+.Xr dhclient-script 8 ,
+.Xr dhcp 8 ,
+.Xr dhcpd 8 ,
+.Xr dhcrelay 8
+.Sh AUTHORS
+.Nm
+was written by
+.An Ted Lemon Aq mellon@fugue.com
+and
+.An Elliot Poger Aq elliot@poger.com .
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
new file mode 100644
index 000000000000..ef353c43724d
--- /dev/null
+++ b/sbin/dhclient/dhclient.c
@@ -0,0 +1,2373 @@
+/* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */
+
+/*
+ * Copyright 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ *
+ * This client was substantially modified and enhanced by Elliot Poger
+ * for use on Linux while he was working on the MosquitoNet project at
+ * Stanford.
+ *
+ * The current version owes much to Elliot's Linux enhancements, but
+ * was substantially reorganized and partially rewritten by Ted Lemon
+ * so as to use the same networking framework that the Internet Software
+ * Consortium DHCP server uses. Much system-specific configuration code
+ * was moved into a shell script so that as support for more operating
+ * systems is added, it will not be necessary to port and maintain
+ * system-specific configuration code to these operating systems - instead,
+ * the shell script can invoke the native tools to accomplish the same
+ * purpose.
+ */
+
+#include "dhcpd.h"
+#include "privsep.h"
+
+#define PERIOD 0x2e
+#define hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
+ ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c))
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+#define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+time_t cur_time;
+time_t default_lease_time = 43200; /* 12 hours... */
+
+char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
+char *path_dhclient_db = NULL;
+
+int log_perror = 1;
+int privfd;
+int nullfd = -1;
+
+struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
+struct in_addr inaddr_any;
+struct sockaddr_in sockaddr_broadcast;
+
+/*
+ * ASSERT_STATE() does nothing now; it used to be
+ * assert (state_is == state_shouldbe).
+ */
+#define ASSERT_STATE(state_is, state_shouldbe) {}
+
+#define TIME_MAX 2147483647
+
+int log_priority;
+int no_daemon;
+int unknown_ok = 1;
+int routefd;
+
+struct interface_info *ifi;
+
+int findproto(char *, int);
+struct sockaddr *get_ifa(char *, int);
+void routehandler(struct protocol *);
+void usage(void);
+int check_option(struct client_lease *l, int option);
+int ipv4addrs(char * buf);
+int res_hnok(const char *dn);
+char *option_as_string(unsigned int code, unsigned char *data, int len);
+int fork_privchld(int, int);
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+time_t scripttime;
+
+int
+findproto(char *cp, int n)
+{
+ struct sockaddr *sa;
+ int i;
+
+ if (n == 0)
+ return -1;
+ for (i = 1; i; i <<= 1) {
+ if (i & n) {
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_IFA:
+ case RTA_DST:
+ case RTA_GATEWAY:
+ case RTA_NETMASK:
+ if (sa->sa_family == AF_INET)
+ return AF_INET;
+ if (sa->sa_family == AF_INET6)
+ return AF_INET6;
+ break;
+ case RTA_IFP:
+ break;
+ }
+ ADVANCE(cp, sa);
+ }
+ }
+ return (-1);
+}
+
+struct sockaddr *
+get_ifa(char *cp, int n)
+{
+ struct sockaddr *sa;
+ int i;
+
+ if (n == 0)
+ return (NULL);
+ for (i = 1; i; i <<= 1)
+ if (i & n) {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_IFA)
+ return (sa);
+ ADVANCE(cp, sa);
+ }
+
+ return (NULL);
+}
+struct iaddr defaddr = { 4 };
+
+/* ARGSUSED */
+void
+routehandler(struct protocol *p)
+{
+ char msg[2048];
+ struct rt_msghdr *rtm;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct if_announcemsghdr *ifan;
+ struct client_lease *l;
+ time_t t = time(NULL);
+ struct sockaddr *sa;
+ struct iaddr a;
+ ssize_t n;
+
+ n = read(routefd, &msg, sizeof(msg));
+ rtm = (struct rt_msghdr *)msg;
+ if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen ||
+ rtm->rtm_version != RTM_VERSION)
+ return;
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if (ifam->ifam_index != ifi->index)
+ break;
+ if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET)
+ break;
+ if (ifi == NULL)
+ goto die;
+ sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs);
+ if (sa == NULL)
+ goto die;
+
+ if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf))
+ error("king bula sez: len mismatch");
+ memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len);
+ if (addr_eq(a, defaddr))
+ break;
+
+ for (l = ifi->client->active; l != NULL; l = l->next)
+ if (addr_eq(a, l->address))
+ break;
+
+ if (l != NULL) /* new addr is the one we set */
+ break;
+
+ goto die;
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if (ifam->ifam_index != ifi->index)
+ break;
+ if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET)
+ break;
+ if (scripttime == 0 || t < scripttime + 10)
+ break;
+ goto die;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ if (ifm->ifm_index != ifi->index)
+ break;
+ if ((rtm->rtm_flags & RTF_UP) == 0)
+ goto die;
+ break;
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *)rtm;
+ if (ifan->ifan_what == IFAN_DEPARTURE &&
+ ifan->ifan_index == ifi->index)
+ goto die;
+ break;
+ default:
+ break;
+ }
+ return;
+
+die:
+ script_init("FAIL", NULL);
+ if (ifi->client->alias)
+ script_write_params("alias_", ifi->client->alias);
+ script_go();
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern char *__progname;
+ int ch, fd, quiet = 0, i = 0;
+ int pipe_fd[2];
+ struct passwd *pw;
+
+ /* Initially, log errors to stderr as well as to syslogd. */
+ openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
+ setlogmask(LOG_UPTO(LOG_INFO));
+
+ while ((ch = getopt(argc, argv, "c:dl:qu")) != -1)
+ switch (ch) {
+ case 'c':
+ path_dhclient_conf = optarg;
+ break;
+ case 'd':
+ no_daemon = 1;
+ break;
+ case 'l':
+ path_dhclient_db = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'u':
+ unknown_ok = 0;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL)
+ error("calloc");
+ if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ)
+ error("Interface name too long");
+ if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s",
+ _PATH_DHCLIENT_DB, ifi->name) == -1)
+ error("asprintf");
+
+ if (quiet)
+ log_perror = 0;
+
+ tzset();
+ time(&cur_time);
+
+ memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
+ sockaddr_broadcast.sin_family = AF_INET;
+ sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
+ sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
+ sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast);
+ inaddr_any.s_addr = INADDR_ANY;
+
+ read_client_conf();
+
+ if (!interface_link_status(ifi->name)) {
+ fprintf(stderr, "%s: no link ...", ifi->name);
+ fflush(stderr);
+ sleep(1);
+ while (!interface_link_status(ifi->name)) {
+ fprintf(stderr, ".");
+ fflush(stderr);
+ if (++i > 10) {
+ fprintf(stderr, " giving up\n");
+ exit(1);
+ }
+ sleep(1);
+ }
+ fprintf(stderr, " got link\n");
+ }
+
+ if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
+ error("cannot open %s: %m", _PATH_DEVNULL);
+
+ if ((pw = getpwnam("_dhcp")) == NULL) {
+ warning("no such user: _dhcp, falling back to \"nobody\"");
+ if ((pw = getpwnam("nobody")) == NULL)
+ error("no such user: nobody");
+ }
+
+ if (pipe(pipe_fd) == -1)
+ error("pipe");
+
+ fork_privchld(pipe_fd[0], pipe_fd[1]);
+
+ close(pipe_fd[0]);
+ privfd = pipe_fd[1];
+
+ if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
+ error("can't open and lock %s: %m", path_dhclient_db);
+ read_client_leases();
+ rewrite_client_leases();
+ close(fd);
+
+ priv_script_init("PREINIT", NULL);
+ if (ifi->client->alias)
+ priv_script_write_params("alias_", ifi->client->alias);
+ priv_script_go();
+
+ if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
+ add_protocol("AF_ROUTE", routefd, routehandler, ifi);
+
+ /* set up the interface */
+ discover_interfaces(ifi);
+
+ if (chroot(_PATH_VAREMPTY) == -1)
+ error("chroot");
+ if (chdir("/") == -1)
+ error("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid))
+ error("can't drop privileges: %m");
+
+ endpwent();
+
+ setproctitle("%s", ifi->name);
+
+ ifi->client->state = S_INIT;
+ state_reboot(ifi);
+
+ bootp_packet_handler = do_packet;
+
+ dispatch();
+
+ /* not reached */
+ return (0);
+}
+
+void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-dqu] ", __progname);
+ fprintf(stderr, "[-c conffile] [-l leasefile] interface\n");
+ exit(1);
+}
+
+/*
+ * Individual States:
+ *
+ * Each routine is called from the dhclient_state_machine() in one of
+ * these conditions:
+ * -> entering INIT state
+ * -> recvpacket_flag == 0: timeout in this state
+ * -> otherwise: received a packet in this state
+ *
+ * Return conditions as handled by dhclient_state_machine():
+ * Returns 1, sendpacket_flag = 1: send packet, reset timer.
+ * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
+ * Returns 0: finish the nap which was interrupted for no good reason.
+ *
+ * Several per-interface variables are used to keep track of the process:
+ * active_lease: the lease that is being used on the interface
+ * (null pointer if not configured yet).
+ * offered_leases: leases corresponding to DHCPOFFER messages that have
+ * been sent to us by DHCP servers.
+ * acked_leases: leases corresponding to DHCPACK messages that have been
+ * sent to us by DHCP servers.
+ * sendpacket: DHCP packet we're trying to send.
+ * destination: IP address to send sendpacket to
+ * In addition, there are several relevant per-lease variables.
+ * T1_expiry, T2_expiry, lease_expiry: lease milestones
+ * In the active lease, these control the process of renewing the lease;
+ * In leases on the acked_leases list, this simply determines when we
+ * can no longer legitimately use the lease.
+ */
+
+void
+state_reboot(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ /* If we don't remember an active lease, go straight to INIT. */
+ if (!ip->client->active || ip->client->active->is_bootp) {
+ state_init(ip);
+ return;
+ }
+
+ /* We are in the rebooting state. */
+ ip->client->state = S_REBOOTING;
+
+ /* make_request doesn't initialize xid because it normally comes
+ from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
+ so pick an xid now. */
+ ip->client->xid = arc4random();
+
+ /* Make a DHCPREQUEST packet, and set appropriate per-interface
+ flags. */
+ make_request(ip, ip->client->active);
+ ip->client->destination = iaddr_broadcast;
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+
+ /* Zap the medium list... */
+ ip->client->medium = NULL;
+
+ /* Send out the first DHCPREQUEST packet. */
+ send_request(ip);
+}
+
+/*
+ * Called when a lease has completely expired and we've
+ * been unable to renew it.
+ */
+void
+state_init(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ ASSERT_STATE(state, S_INIT);
+
+ /* Make a DHCPDISCOVER packet, and set appropriate per-interface
+ flags. */
+ make_discover(ip, ip->client->active);
+ ip->client->xid = ip->client->packet.xid;
+ ip->client->destination = iaddr_broadcast;
+ ip->client->state = S_SELECTING;
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+
+ /* Add an immediate timeout to cause the first DHCPDISCOVER packet
+ to go out. */
+ send_discover(ip);
+}
+
+/*
+ * state_selecting is called when one or more DHCPOFFER packets
+ * have been received and a configurable period of time has passed.
+ */
+void
+state_selecting(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ struct client_lease *lp, *next, *picked;
+
+ ASSERT_STATE(state, S_SELECTING);
+
+ /* Cancel state_selecting and send_discover timeouts, since either
+ one could have got us here. */
+ cancel_timeout(state_selecting, ip);
+ cancel_timeout(send_discover, ip);
+
+ /* We have received one or more DHCPOFFER packets. Currently,
+ the only criterion by which we judge leases is whether or
+ not we get a response when we arp for them. */
+ picked = NULL;
+ for (lp = ip->client->offered_leases; lp; lp = next) {
+ next = lp->next;
+
+ /* Check to see if we got an ARPREPLY for the address
+ in this particular lease. */
+ if (!picked) {
+ script_init("ARPCHECK", lp->medium);
+ script_write_params("check_", lp);
+
+ /* If the ARPCHECK code detects another
+ machine using the offered address, it exits
+ nonzero. We need to send a DHCPDECLINE and
+ toss the lease. */
+ if (script_go()) {
+ make_decline(ip, lp);
+ send_decline(ip);
+ goto freeit;
+ }
+ picked = lp;
+ picked->next = NULL;
+ } else {
+freeit:
+ free_client_lease(lp);
+ }
+ }
+ ip->client->offered_leases = NULL;
+
+ /* If we just tossed all the leases we were offered, go back
+ to square one. */
+ if (!picked) {
+ ip->client->state = S_INIT;
+ state_init(ip);
+ return;
+ }
+
+ /* If it was a BOOTREPLY, we can just take the address right now. */
+ if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
+ ip->client->new = picked;
+
+ /* Make up some lease expiry times
+ XXX these should be configurable. */
+ ip->client->new->expiry = cur_time + 12000;
+ ip->client->new->renewal += cur_time + 8000;
+ ip->client->new->rebind += cur_time + 10000;
+
+ ip->client->state = S_REQUESTING;
+
+ /* Bind to the address we received. */
+ bind_lease(ip);
+ return;
+ }
+
+ /* Go to the REQUESTING state. */
+ ip->client->destination = iaddr_broadcast;
+ ip->client->state = S_REQUESTING;
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+
+ /* Make a DHCPREQUEST packet from the lease we picked. */
+ make_request(ip, picked);
+ ip->client->xid = ip->client->packet.xid;
+
+ /* Toss the lease we picked - we'll get it back in a DHCPACK. */
+ free_client_lease(picked);
+
+ /* Add an immediate timeout to send the first DHCPREQUEST packet. */
+ send_request(ip);
+}
+
+/* state_requesting is called when we receive a DHCPACK message after
+ having sent out one or more DHCPREQUEST packets. */
+
+void
+dhcpack(struct packet *packet)
+{
+ struct interface_info *ip = packet->interface;
+ struct client_lease *lease;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (packet->interface->client->xid != packet->raw->xid ||
+ (packet->interface->hw_address.hlen != packet->raw->hlen) ||
+ (memcmp(packet->interface->hw_address.haddr,
+ packet->raw->chaddr, packet->raw->hlen)))
+ return;
+
+ if (ip->client->state != S_REBOOTING &&
+ ip->client->state != S_REQUESTING &&
+ ip->client->state != S_RENEWING &&
+ ip->client->state != S_REBINDING)
+ return;
+
+ note("DHCPACK from %s", piaddr(packet->client_addr));
+
+ lease = packet_to_lease(packet);
+ if (!lease) {
+ note("packet_to_lease failed.");
+ return;
+ }
+
+ ip->client->new = lease;
+
+ /* Stop resending DHCPREQUEST. */
+ cancel_timeout(send_request, ip);
+
+ /* Figure out the lease time. */
+ if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
+ ip->client->new->expiry = getULong(
+ ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
+ else
+ ip->client->new->expiry = default_lease_time;
+ /* A number that looks negative here is really just very large,
+ because the lease expiry offset is unsigned. */
+ if (ip->client->new->expiry < 0)
+ ip->client->new->expiry = TIME_MAX;
+ /* XXX should be fixed by resetting the client state */
+ if (ip->client->new->expiry < 60)
+ ip->client->new->expiry = 60;
+
+ /* Take the server-provided renewal time if there is one;
+ otherwise figure it out according to the spec. */
+ if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
+ ip->client->new->renewal = getULong(
+ ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
+ else
+ ip->client->new->renewal = ip->client->new->expiry / 2;
+
+ /* Same deal with the rebind time. */
+ if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
+ ip->client->new->rebind = getULong(
+ ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
+ else
+ ip->client->new->rebind = ip->client->new->renewal +
+ ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
+
+ ip->client->new->expiry += cur_time;
+ /* Lease lengths can never be negative. */
+ if (ip->client->new->expiry < cur_time)
+ ip->client->new->expiry = TIME_MAX;
+ ip->client->new->renewal += cur_time;
+ if (ip->client->new->renewal < cur_time)
+ ip->client->new->renewal = TIME_MAX;
+ ip->client->new->rebind += cur_time;
+ if (ip->client->new->rebind < cur_time)
+ ip->client->new->rebind = TIME_MAX;
+
+ bind_lease(ip);
+}
+
+void
+bind_lease(struct interface_info *ip)
+{
+ /* Remember the medium. */
+ ip->client->new->medium = ip->client->medium;
+
+ /* Write out the new lease. */
+ write_client_lease(ip, ip->client->new, 0);
+
+ /* Run the client script with the new parameters. */
+ script_init((ip->client->state == S_REQUESTING ? "BOUND" :
+ (ip->client->state == S_RENEWING ? "RENEW" :
+ (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))),
+ ip->client->new->medium);
+ if (ip->client->active && ip->client->state != S_REBOOTING)
+ script_write_params("old_", ip->client->active);
+ script_write_params("new_", ip->client->new);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+
+ /* Replace the old active lease with the new one. */
+ if (ip->client->active)
+ free_client_lease(ip->client->active);
+ ip->client->active = ip->client->new;
+ ip->client->new = NULL;
+
+ /* Set up a timeout to start the renewal process. */
+ add_timeout(ip->client->active->renewal, state_bound, ip);
+
+ note("bound to %s -- renewal in %d seconds.",
+ piaddr(ip->client->active->address),
+ ip->client->active->renewal - cur_time);
+ ip->client->state = S_BOUND;
+ reinitialize_interfaces();
+ go_daemon();
+}
+
+/*
+ * state_bound is called when we've successfully bound to a particular
+ * lease, but the renewal time on that lease has expired. We are
+ * expected to unicast a DHCPREQUEST to the server that gave us our
+ * original lease.
+ */
+void
+state_bound(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ ASSERT_STATE(state, S_BOUND);
+
+ /* T1 has expired. */
+ make_request(ip, ip->client->active);
+ ip->client->xid = ip->client->packet.xid;
+
+ if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
+ memcpy(ip->client->destination.iabuf, ip->client->active->
+ options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
+ ip->client->destination.len = 4;
+ } else
+ ip->client->destination = iaddr_broadcast;
+
+ ip->client->first_sending = cur_time;
+ ip->client->interval = ip->client->config->initial_interval;
+ ip->client->state = S_RENEWING;
+
+ /* Send the first packet immediately. */
+ send_request(ip);
+}
+
+void
+bootp(struct packet *packet)
+{
+ struct iaddrlist *ap;
+
+ if (packet->raw->op != BOOTREPLY)
+ return;
+
+ /* If there's a reject list, make sure this packet's sender isn't
+ on it. */
+ for (ap = packet->interface->client->config->reject_list;
+ ap; ap = ap->next) {
+ if (addr_eq(packet->client_addr, ap->addr)) {
+ note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
+ return;
+ }
+ }
+ dhcpoffer(packet);
+}
+
+void
+dhcp(struct packet *packet)
+{
+ struct iaddrlist *ap;
+ void (*handler)(struct packet *);
+ char *type;
+
+ switch (packet->packet_type) {
+ case DHCPOFFER:
+ handler = dhcpoffer;
+ type = "DHCPOFFER";
+ break;
+ case DHCPNAK:
+ handler = dhcpnak;
+ type = "DHCPNACK";
+ break;
+ case DHCPACK:
+ handler = dhcpack;
+ type = "DHCPACK";
+ break;
+ default:
+ return;
+ }
+
+ /* If there's a reject list, make sure this packet's sender isn't
+ on it. */
+ for (ap = packet->interface->client->config->reject_list;
+ ap; ap = ap->next) {
+ if (addr_eq(packet->client_addr, ap->addr)) {
+ note("%s from %s rejected.", type, piaddr(ap->addr));
+ return;
+ }
+ }
+ (*handler)(packet);
+}
+
+void
+dhcpoffer(struct packet *packet)
+{
+ struct interface_info *ip = packet->interface;
+ struct client_lease *lease, *lp;
+ int i;
+ int arp_timeout_needed, stop_selecting;
+ char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
+ "DHCPOFFER" : "BOOTREPLY";
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (ip->client->state != S_SELECTING ||
+ packet->interface->client->xid != packet->raw->xid ||
+ (packet->interface->hw_address.hlen != packet->raw->hlen) ||
+ (memcmp(packet->interface->hw_address.haddr,
+ packet->raw->chaddr, packet->raw->hlen)))
+ return;
+
+ note("%s from %s", name, piaddr(packet->client_addr));
+
+
+ /* If this lease doesn't supply the minimum required parameters,
+ blow it off. */
+ for (i = 0; ip->client->config->required_options[i]; i++) {
+ if (!packet->options[ip->client->config->
+ required_options[i]].len) {
+ note("%s isn't satisfactory.", name);
+ return;
+ }
+ }
+
+ /* If we've already seen this lease, don't record it again. */
+ for (lease = ip->client->offered_leases;
+ lease; lease = lease->next) {
+ if (lease->address.len == sizeof(packet->raw->yiaddr) &&
+ !memcmp(lease->address.iabuf,
+ &packet->raw->yiaddr, lease->address.len)) {
+ debug("%s already seen.", name);
+ return;
+ }
+ }
+
+ lease = packet_to_lease(packet);
+ if (!lease) {
+ note("packet_to_lease failed.");
+ return;
+ }
+
+ /* If this lease was acquired through a BOOTREPLY, record that
+ fact. */
+ if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
+ lease->is_bootp = 1;
+
+ /* Record the medium under which this lease was offered. */
+ lease->medium = ip->client->medium;
+
+ /* Send out an ARP Request for the offered IP address. */
+ script_init("ARPSEND", lease->medium);
+ script_write_params("check_", lease);
+ /* If the script can't send an ARP request without waiting,
+ we'll be waiting when we do the ARPCHECK, so don't wait now. */
+ if (script_go())
+ arp_timeout_needed = 0;
+ else
+ arp_timeout_needed = 2;
+
+ /* Figure out when we're supposed to stop selecting. */
+ stop_selecting =
+ ip->client->first_sending + ip->client->config->select_interval;
+
+ /* If this is the lease we asked for, put it at the head of the
+ list, and don't mess with the arp request timeout. */
+ if (lease->address.len == ip->client->requested_address.len &&
+ !memcmp(lease->address.iabuf,
+ ip->client->requested_address.iabuf,
+ ip->client->requested_address.len)) {
+ lease->next = ip->client->offered_leases;
+ ip->client->offered_leases = lease;
+ } else {
+ /* If we already have an offer, and arping for this
+ offer would take us past the selection timeout,
+ then don't extend the timeout - just hope for the
+ best. */
+ if (ip->client->offered_leases &&
+ (cur_time + arp_timeout_needed) > stop_selecting)
+ arp_timeout_needed = 0;
+
+ /* Put the lease at the end of the list. */
+ lease->next = NULL;
+ if (!ip->client->offered_leases)
+ ip->client->offered_leases = lease;
+ else {
+ for (lp = ip->client->offered_leases; lp->next;
+ lp = lp->next)
+ ; /* nothing */
+ lp->next = lease;
+ }
+ }
+
+ /* If we're supposed to stop selecting before we've had time
+ to wait for the ARPREPLY, add some delay to wait for
+ the ARPREPLY. */
+ if (stop_selecting - cur_time < arp_timeout_needed)
+ stop_selecting = cur_time + arp_timeout_needed;
+
+ /* If the selecting interval has expired, go immediately to
+ state_selecting(). Otherwise, time out into
+ state_selecting at the select interval. */
+ if (stop_selecting <= 0)
+ state_selecting(ip);
+ else {
+ add_timeout(stop_selecting, state_selecting, ip);
+ cancel_timeout(send_discover, ip);
+ }
+}
+
+/* Allocate a client_lease structure and initialize it from the parameters
+ in the specified packet. */
+
+struct client_lease *
+packet_to_lease(struct packet *packet)
+{
+ struct client_lease *lease;
+ int i;
+
+ lease = malloc(sizeof(struct client_lease));
+
+ if (!lease) {
+ warning("dhcpoffer: no memory to record lease.");
+ return (NULL);
+ }
+
+ memset(lease, 0, sizeof(*lease));
+
+ /* Copy the lease options. */
+ for (i = 0; i < 256; i++) {
+ if (packet->options[i].len) {
+ lease->options[i].data =
+ malloc(packet->options[i].len + 1);
+ if (!lease->options[i].data) {
+ warning("dhcpoffer: no memory for option %d", i);
+ free_client_lease(lease);
+ return (NULL);
+ } else {
+ memcpy(lease->options[i].data,
+ packet->options[i].data,
+ packet->options[i].len);
+ lease->options[i].len =
+ packet->options[i].len;
+ lease->options[i].data[lease->options[i].len] =
+ 0;
+ }
+ if (!check_option(lease,i)) {
+ /* ignore a bogus lease offer */
+ warning("Invalid lease option - ignoring offer");
+ free_client_lease(lease);
+ return (NULL);
+ }
+ }
+ }
+
+ lease->address.len = sizeof(packet->raw->yiaddr);
+ memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
+
+ /* If the server name was filled out, copy it. */
+ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
+ !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
+ packet->raw->sname[0]) {
+ lease->server_name = malloc(DHCP_SNAME_LEN + 1);
+ if (!lease->server_name) {
+ warning("dhcpoffer: no memory for server name.");
+ free_client_lease(lease);
+ return (NULL);
+ }
+ memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
+ lease->server_name[DHCP_SNAME_LEN]='\0';
+ if (!res_hnok(lease->server_name) ) {
+ warning("Bogus server name %s", lease->server_name );
+ free_client_lease(lease);
+ return (NULL);
+ }
+
+ }
+
+ /* Ditto for the filename. */
+ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
+ !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
+ packet->raw->file[0]) {
+ /* Don't count on the NUL terminator. */
+ lease->filename = malloc(DHCP_FILE_LEN + 1);
+ if (!lease->filename) {
+ warning("dhcpoffer: no memory for filename.");
+ free_client_lease(lease);
+ return (NULL);
+ }
+ memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
+ lease->filename[DHCP_FILE_LEN]='\0';
+ }
+ return lease;
+}
+
+void
+dhcpnak(struct packet *packet)
+{
+ struct interface_info *ip = packet->interface;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (packet->interface->client->xid != packet->raw->xid ||
+ (packet->interface->hw_address.hlen != packet->raw->hlen) ||
+ (memcmp(packet->interface->hw_address.haddr,
+ packet->raw->chaddr, packet->raw->hlen)))
+ return;
+
+ if (ip->client->state != S_REBOOTING &&
+ ip->client->state != S_REQUESTING &&
+ ip->client->state != S_RENEWING &&
+ ip->client->state != S_REBINDING)
+ return;
+
+ note("DHCPNAK from %s", piaddr(packet->client_addr));
+
+ if (!ip->client->active) {
+ note("DHCPNAK with no active lease.\n");
+ return;
+ }
+
+ free_client_lease(ip->client->active);
+ ip->client->active = NULL;
+
+ /* Stop sending DHCPREQUEST packets... */
+ cancel_timeout(send_request, ip);
+
+ ip->client->state = S_INIT;
+ state_init(ip);
+}
+
+/* Send out a DHCPDISCOVER packet, and set a timeout to send out another
+ one after the right interval has expired. If we don't get an offer by
+ the time we reach the panic interval, call the panic function. */
+
+void
+send_discover(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ int interval, increase = 1;
+
+ /* Figure out how long it's been since we started transmitting. */
+ interval = cur_time - ip->client->first_sending;
+
+ /* If we're past the panic timeout, call the script and tell it
+ we haven't found anything for this interface yet. */
+ if (interval > ip->client->config->timeout) {
+ state_panic(ip);
+ return;
+ }
+
+ /* If we're selecting media, try the whole list before doing
+ the exponential backoff, but if we've already received an
+ offer, stop looping, because we obviously have it right. */
+ if (!ip->client->offered_leases &&
+ ip->client->config->media) {
+ int fail = 0;
+again:
+ if (ip->client->medium) {
+ ip->client->medium = ip->client->medium->next;
+ increase = 0;
+ }
+ if (!ip->client->medium) {
+ if (fail)
+ error("No valid media types for %s!", ip->name);
+ ip->client->medium = ip->client->config->media;
+ increase = 1;
+ }
+
+ note("Trying medium \"%s\" %d", ip->client->medium->string,
+ increase);
+ script_init("MEDIUM", ip->client->medium);
+ if (script_go())
+ goto again;
+ }
+
+ /*
+ * If we're supposed to increase the interval, do so. If it's
+ * currently zero (i.e., we haven't sent any packets yet), set
+ * it to one; otherwise, add to it a random number between zero
+ * and two times itself. On average, this means that it will
+ * double with every transmission.
+ */
+ if (increase) {
+ if (!ip->client->interval)
+ ip->client->interval =
+ ip->client->config->initial_interval;
+ else {
+ ip->client->interval += (arc4random() >> 2) %
+ (2 * ip->client->interval);
+ }
+
+ /* Don't backoff past cutoff. */
+ if (ip->client->interval >
+ ip->client->config->backoff_cutoff)
+ ip->client->interval =
+ ((ip->client->config->backoff_cutoff / 2)
+ + ((arc4random() >> 2) %
+ ip->client->config->backoff_cutoff));
+ } else if (!ip->client->interval)
+ ip->client->interval =
+ ip->client->config->initial_interval;
+
+ /* If the backoff would take us to the panic timeout, just use that
+ as the interval. */
+ if (cur_time + ip->client->interval >
+ ip->client->first_sending + ip->client->config->timeout)
+ ip->client->interval =
+ (ip->client->first_sending +
+ ip->client->config->timeout) - cur_time + 1;
+
+ /* Record the number of seconds since we started sending. */
+ if (interval < 65536)
+ ip->client->packet.secs = htons(interval);
+ else
+ ip->client->packet.secs = htons(65535);
+ ip->client->secs = ip->client->packet.secs;
+
+ note("DHCPDISCOVER on %s to %s port %d interval %d",
+ ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
+ ntohs(sockaddr_broadcast.sin_port), ip->client->interval);
+
+ /* Send out a packet. */
+ (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
+ inaddr_any, &sockaddr_broadcast, NULL);
+
+ add_timeout(cur_time + ip->client->interval, send_discover, ip);
+}
+
+/*
+ * state_panic gets called if we haven't received any offers in a preset
+ * amount of time. When this happens, we try to use existing leases
+ * that haven't yet expired, and failing that, we call the client script
+ * and hope it can do something.
+ */
+void
+state_panic(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ struct client_lease *loop = ip->client->active;
+ struct client_lease *lp;
+
+ note("No DHCPOFFERS received.");
+
+ /* We may not have an active lease, but we may have some
+ predefined leases that we can try. */
+ if (!ip->client->active && ip->client->leases)
+ goto activate_next;
+
+ /* Run through the list of leases and see if one can be used. */
+ while (ip->client->active) {
+ if (ip->client->active->expiry > cur_time) {
+ note("Trying recorded lease %s",
+ piaddr(ip->client->active->address));
+ /* Run the client script with the existing
+ parameters. */
+ script_init("TIMEOUT",
+ ip->client->active->medium);
+ script_write_params("new_", ip->client->active);
+ if (ip->client->alias)
+ script_write_params("alias_",
+ ip->client->alias);
+
+ /* If the old lease is still good and doesn't
+ yet need renewal, go into BOUND state and
+ timeout at the renewal time. */
+ if (!script_go()) {
+ if (cur_time <
+ ip->client->active->renewal) {
+ ip->client->state = S_BOUND;
+ note("bound: renewal in %d seconds.",
+ ip->client->active->renewal -
+ cur_time);
+ add_timeout(
+ ip->client->active->renewal,
+ state_bound, ip);
+ } else {
+ ip->client->state = S_BOUND;
+ note("bound: immediate renewal.");
+ state_bound(ip);
+ }
+ reinitialize_interfaces();
+ go_daemon();
+ return;
+ }
+ }
+
+ /* If there are no other leases, give up. */
+ if (!ip->client->leases) {
+ ip->client->leases = ip->client->active;
+ ip->client->active = NULL;
+ break;
+ }
+
+activate_next:
+ /* Otherwise, put the active lease at the end of the
+ lease list, and try another lease.. */
+ for (lp = ip->client->leases; lp->next; lp = lp->next)
+ ;
+ lp->next = ip->client->active;
+ if (lp->next)
+ lp->next->next = NULL;
+ ip->client->active = ip->client->leases;
+ ip->client->leases = ip->client->leases->next;
+
+ /* If we already tried this lease, we've exhausted the
+ set of leases, so we might as well give up for
+ now. */
+ if (ip->client->active == loop)
+ break;
+ else if (!loop)
+ loop = ip->client->active;
+ }
+
+ /* No leases were available, or what was available didn't work, so
+ tell the shell script that we failed to allocate an address,
+ and try again later. */
+ note("No working leases in persistent database - sleeping.\n");
+ script_init("FAIL", NULL);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+ ip->client->state = S_INIT;
+ add_timeout(cur_time + ip->client->config->retry_interval, state_init,
+ ip);
+ go_daemon();
+}
+
+void
+send_request(void *ipp)
+{
+ struct interface_info *ip = ipp;
+ struct sockaddr_in destination;
+ struct in_addr from;
+ int interval;
+
+ /* Figure out how long it's been since we started transmitting. */
+ interval = cur_time - ip->client->first_sending;
+
+ /* If we're in the INIT-REBOOT or REQUESTING state and we're
+ past the reboot timeout, go to INIT and see if we can
+ DISCOVER an address... */
+ /* XXX In the INIT-REBOOT state, if we don't get an ACK, it
+ means either that we're on a network with no DHCP server,
+ or that our server is down. In the latter case, assuming
+ that there is a backup DHCP server, DHCPDISCOVER will get
+ us a new address, but we could also have successfully
+ reused our old address. In the former case, we're hosed
+ anyway. This is not a win-prone situation. */
+ if ((ip->client->state == S_REBOOTING ||
+ ip->client->state == S_REQUESTING) &&
+ interval > ip->client->config->reboot_timeout) {
+cancel:
+ ip->client->state = S_INIT;
+ cancel_timeout(send_request, ip);
+ state_init(ip);
+ return;
+ }
+
+ /* If we're in the reboot state, make sure the media is set up
+ correctly. */
+ if (ip->client->state == S_REBOOTING &&
+ !ip->client->medium &&
+ ip->client->active->medium ) {
+ script_init("MEDIUM", ip->client->active->medium);
+
+ /* If the medium we chose won't fly, go to INIT state. */
+ if (script_go())
+ goto cancel;
+
+ /* Record the medium. */
+ ip->client->medium = ip->client->active->medium;
+ }
+
+ /* If the lease has expired, relinquish the address and go back
+ to the INIT state. */
+ if (ip->client->state != S_REQUESTING &&
+ cur_time > ip->client->active->expiry) {
+ /* Run the client script with the new parameters. */
+ script_init("EXPIRE", NULL);
+ script_write_params("old_", ip->client->active);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+
+ /* Now do a preinit on the interface so that we can
+ discover a new address. */
+ script_init("PREINIT", NULL);
+ if (ip->client->alias)
+ script_write_params("alias_", ip->client->alias);
+ script_go();
+
+ ip->client->state = S_INIT;
+ state_init(ip);
+ return;
+ }
+
+ /* Do the exponential backoff... */
+ if (!ip->client->interval)
+ ip->client->interval = ip->client->config->initial_interval;
+ else
+ ip->client->interval += ((arc4random() >> 2) %
+ (2 * ip->client->interval));
+
+ /* Don't backoff past cutoff. */
+ if (ip->client->interval >
+ ip->client->config->backoff_cutoff)
+ ip->client->interval =
+ ((ip->client->config->backoff_cutoff / 2) +
+ ((arc4random() >> 2) % ip->client->interval));
+
+ /* If the backoff would take us to the expiry time, just set the
+ timeout to the expiry time. */
+ if (ip->client->state != S_REQUESTING &&
+ cur_time + ip->client->interval >
+ ip->client->active->expiry)
+ ip->client->interval =
+ ip->client->active->expiry - cur_time + 1;
+
+ /* If the lease T2 time has elapsed, or if we're not yet bound,
+ broadcast the DHCPREQUEST rather than unicasting. */
+ memset(&destination, 0, sizeof(destination));
+ if (ip->client->state == S_REQUESTING ||
+ ip->client->state == S_REBOOTING ||
+ cur_time > ip->client->active->rebind)
+ destination.sin_addr.s_addr = INADDR_BROADCAST;
+ else
+ memcpy(&destination.sin_addr.s_addr,
+ ip->client->destination.iabuf,
+ sizeof(destination.sin_addr.s_addr));
+ destination.sin_port = htons(REMOTE_PORT);
+ destination.sin_family = AF_INET;
+ destination.sin_len = sizeof(destination);
+
+ if (ip->client->state != S_REQUESTING)
+ memcpy(&from, ip->client->active->address.iabuf,
+ sizeof(from));
+ else
+ from.s_addr = INADDR_ANY;
+
+ /* Record the number of seconds since we started sending. */
+ if (ip->client->state == S_REQUESTING)
+ ip->client->packet.secs = ip->client->secs;
+ else {
+ if (interval < 65536)
+ ip->client->packet.secs = htons(interval);
+ else
+ ip->client->packet.secs = htons(65535);
+ }
+
+ note("DHCPREQUEST on %s to %s port %d", ip->name,
+ inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
+
+ /* Send out a packet. */
+ (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
+ from, &destination, NULL);
+
+ add_timeout(cur_time + ip->client->interval, send_request, ip);
+}
+
+void
+send_decline(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ note("DHCPDECLINE on %s to %s port %d", ip->name,
+ inet_ntoa(sockaddr_broadcast.sin_addr),
+ ntohs(sockaddr_broadcast.sin_port));
+
+ /* Send out a packet. */
+ (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
+ inaddr_any, &sockaddr_broadcast, NULL);
+}
+
+void
+make_discover(struct interface_info *ip, struct client_lease *lease)
+{
+ unsigned char discover = DHCPDISCOVER;
+ struct tree_cache *options[256];
+ struct tree_cache option_elements[256];
+ int i;
+
+ memset(option_elements, 0, sizeof(option_elements));
+ memset(options, 0, sizeof(options));
+ memset(&ip->client->packet, 0, sizeof(ip->client->packet));
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ options[i] = &option_elements[i];
+ options[i]->value = &discover;
+ options[i]->len = sizeof(discover);
+ options[i]->buf_size = sizeof(discover);
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Request the options we want */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ options[i] = &option_elements[i];
+ options[i]->value = ip->client->config->requested_options;
+ options[i]->len = ip->client->config->requested_option_count;
+ options[i]->buf_size =
+ ip->client->config->requested_option_count;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* If we had an address, try to get it again. */
+ if (lease) {
+ ip->client->requested_address = lease->address;
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &option_elements[i];
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+ } else
+ ip->client->requested_address.len = 0;
+
+ /* Send any options requested in the config file. */
+ for (i = 0; i < 256; i++)
+ if (!options[i] &&
+ ip->client->config->send_options[i].data) {
+ options[i] = &option_elements[i];
+ options[i]->value =
+ ip->client->config->send_options[i].data;
+ options[i]->len =
+ ip->client->config->send_options[i].len;
+ options[i]->buf_size =
+ ip->client->config->send_options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+
+ /* Set up the option buffer... */
+ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
+ options, 0, 0, 0, NULL, 0);
+ if (ip->client->packet_length < BOOTP_MIN_LEN)
+ ip->client->packet_length = BOOTP_MIN_LEN;
+
+ ip->client->packet.op = BOOTREQUEST;
+ ip->client->packet.htype = ip->hw_address.htype;
+ ip->client->packet.hlen = ip->hw_address.hlen;
+ ip->client->packet.hops = 0;
+ ip->client->packet.xid = arc4random();
+ ip->client->packet.secs = 0; /* filled in by send_discover. */
+ ip->client->packet.flags = 0;
+
+ memset(&(ip->client->packet.ciaddr),
+ 0, sizeof(ip->client->packet.ciaddr));
+ memset(&(ip->client->packet.yiaddr),
+ 0, sizeof(ip->client->packet.yiaddr));
+ memset(&(ip->client->packet.siaddr),
+ 0, sizeof(ip->client->packet.siaddr));
+ memset(&(ip->client->packet.giaddr),
+ 0, sizeof(ip->client->packet.giaddr));
+ memcpy(ip->client->packet.chaddr,
+ ip->hw_address.haddr, ip->hw_address.hlen);
+}
+
+
+void
+make_request(struct interface_info *ip, struct client_lease * lease)
+{
+ unsigned char request = DHCPREQUEST;
+ struct tree_cache *options[256];
+ struct tree_cache option_elements[256];
+ int i;
+
+ memset(options, 0, sizeof(options));
+ memset(&ip->client->packet, 0, sizeof(ip->client->packet));
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ options[i] = &option_elements[i];
+ options[i]->value = &request;
+ options[i]->len = sizeof(request);
+ options[i]->buf_size = sizeof(request);
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Request the options we want */
+ i = DHO_DHCP_PARAMETER_REQUEST_LIST;
+ options[i] = &option_elements[i];
+ options[i]->value = ip->client->config->requested_options;
+ options[i]->len = ip->client->config->requested_option_count;
+ options[i]->buf_size =
+ ip->client->config->requested_option_count;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* If we are requesting an address that hasn't yet been assigned
+ to us, use the DHCP Requested Address option. */
+ if (ip->client->state == S_REQUESTING) {
+ /* Send back the server identifier... */
+ i = DHO_DHCP_SERVER_IDENTIFIER;
+ options[i] = &option_elements[i];
+ options[i]->value = lease->options[i].data;
+ options[i]->len = lease->options[i].len;
+ options[i]->buf_size = lease->options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+ if (ip->client->state == S_REQUESTING ||
+ ip->client->state == S_REBOOTING) {
+ ip->client->requested_address = lease->address;
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &option_elements[i];
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+ } else
+ ip->client->requested_address.len = 0;
+
+ /* Send any options requested in the config file. */
+ for (i = 0; i < 256; i++)
+ if (!options[i] &&
+ ip->client->config->send_options[i].data) {
+ options[i] = &option_elements[i];
+ options[i]->value =
+ ip->client->config->send_options[i].data;
+ options[i]->len =
+ ip->client->config->send_options[i].len;
+ options[i]->buf_size =
+ ip->client->config->send_options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+
+ /* Set up the option buffer... */
+ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
+ options, 0, 0, 0, NULL, 0);
+ if (ip->client->packet_length < BOOTP_MIN_LEN)
+ ip->client->packet_length = BOOTP_MIN_LEN;
+
+ ip->client->packet.op = BOOTREQUEST;
+ ip->client->packet.htype = ip->hw_address.htype;
+ ip->client->packet.hlen = ip->hw_address.hlen;
+ ip->client->packet.hops = 0;
+ ip->client->packet.xid = ip->client->xid;
+ ip->client->packet.secs = 0; /* Filled in by send_request. */
+
+ /* If we own the address we're requesting, put it in ciaddr;
+ otherwise set ciaddr to zero. */
+ if (ip->client->state == S_BOUND ||
+ ip->client->state == S_RENEWING ||
+ ip->client->state == S_REBINDING) {
+ memcpy(&ip->client->packet.ciaddr,
+ lease->address.iabuf, lease->address.len);
+ ip->client->packet.flags = 0;
+ } else {
+ memset(&ip->client->packet.ciaddr, 0,
+ sizeof(ip->client->packet.ciaddr));
+ ip->client->packet.flags = 0;
+ }
+
+ memset(&ip->client->packet.yiaddr, 0,
+ sizeof(ip->client->packet.yiaddr));
+ memset(&ip->client->packet.siaddr, 0,
+ sizeof(ip->client->packet.siaddr));
+ memset(&ip->client->packet.giaddr, 0,
+ sizeof(ip->client->packet.giaddr));
+ memcpy(ip->client->packet.chaddr,
+ ip->hw_address.haddr, ip->hw_address.hlen);
+}
+
+void
+make_decline(struct interface_info *ip, struct client_lease *lease)
+{
+ struct tree_cache *options[256], message_type_tree;
+ struct tree_cache requested_address_tree;
+ struct tree_cache server_id_tree, client_id_tree;
+ unsigned char decline = DHCPDECLINE;
+ int i;
+
+ memset(options, 0, sizeof(options));
+ memset(&ip->client->packet, 0, sizeof(ip->client->packet));
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ options[i] = &message_type_tree;
+ options[i]->value = &decline;
+ options[i]->len = sizeof(decline);
+ options[i]->buf_size = sizeof(decline);
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Send back the server identifier... */
+ i = DHO_DHCP_SERVER_IDENTIFIER;
+ options[i] = &server_id_tree;
+ options[i]->value = lease->options[i].data;
+ options[i]->len = lease->options[i].len;
+ options[i]->buf_size = lease->options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Send back the address we're declining. */
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &requested_address_tree;
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ /* Send the uid if the user supplied one. */
+ i = DHO_DHCP_CLIENT_IDENTIFIER;
+ if (ip->client->config->send_options[i].len) {
+ options[i] = &client_id_tree;
+ options[i]->value = ip->client->config->send_options[i].data;
+ options[i]->len = ip->client->config->send_options[i].len;
+ options[i]->buf_size = ip->client->config->send_options[i].len;
+ options[i]->timeout = 0xFFFFFFFF;
+ }
+
+
+ /* Set up the option buffer... */
+ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
+ options, 0, 0, 0, NULL, 0);
+ if (ip->client->packet_length < BOOTP_MIN_LEN)
+ ip->client->packet_length = BOOTP_MIN_LEN;
+
+ ip->client->packet.op = BOOTREQUEST;
+ ip->client->packet.htype = ip->hw_address.htype;
+ ip->client->packet.hlen = ip->hw_address.hlen;
+ ip->client->packet.hops = 0;
+ ip->client->packet.xid = ip->client->xid;
+ ip->client->packet.secs = 0; /* Filled in by send_request. */
+ ip->client->packet.flags = 0;
+
+ /* ciaddr must always be zero. */
+ memset(&ip->client->packet.ciaddr, 0,
+ sizeof(ip->client->packet.ciaddr));
+ memset(&ip->client->packet.yiaddr, 0,
+ sizeof(ip->client->packet.yiaddr));
+ memset(&ip->client->packet.siaddr, 0,
+ sizeof(ip->client->packet.siaddr));
+ memset(&ip->client->packet.giaddr, 0,
+ sizeof(ip->client->packet.giaddr));
+ memcpy(ip->client->packet.chaddr,
+ ip->hw_address.haddr, ip->hw_address.hlen);
+}
+
+void
+free_client_lease(struct client_lease *lease)
+{
+ int i;
+
+ if (lease->server_name)
+ free(lease->server_name);
+ if (lease->filename)
+ free(lease->filename);
+ for (i = 0; i < 256; i++) {
+ if (lease->options[i].len)
+ free(lease->options[i].data);
+ }
+ free(lease);
+}
+
+FILE *leaseFile;
+
+void
+rewrite_client_leases(void)
+{
+ struct client_lease *lp;
+
+ if (!leaseFile) {
+ leaseFile = fopen(path_dhclient_db, "w");
+ if (!leaseFile)
+ error("can't create %s: %m", path_dhclient_db);
+ } else {
+ fflush(leaseFile);
+ rewind(leaseFile);
+ }
+
+ for (lp = ifi->client->leases; lp; lp = lp->next)
+ write_client_lease(ifi, lp, 1);
+ if (ifi->client->active)
+ write_client_lease(ifi, ifi->client->active, 1);
+
+ fflush(leaseFile);
+ ftruncate(fileno(leaseFile), ftello(leaseFile));
+ fsync(fileno(leaseFile));
+}
+
+void
+write_client_lease(struct interface_info *ip, struct client_lease *lease,
+ int rewrite)
+{
+ static int leases_written;
+ struct tm *t;
+ int i;
+
+ if (!rewrite) {
+ if (leases_written++ > 20) {
+ rewrite_client_leases();
+ leases_written = 0;
+ }
+ }
+
+ /* If the lease came from the config file, we don't need to stash
+ a copy in the lease database. */
+ if (lease->is_static)
+ return;
+
+ if (!leaseFile) { /* XXX */
+ leaseFile = fopen(path_dhclient_db, "w");
+ if (!leaseFile)
+ error("can't create %s: %m", path_dhclient_db);
+ }
+
+ fprintf(leaseFile, "lease {\n");
+ if (lease->is_bootp)
+ fprintf(leaseFile, " bootp;\n");
+ fprintf(leaseFile, " interface \"%s\";\n", ip->name);
+ fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address));
+ if (lease->filename)
+ fprintf(leaseFile, " filename \"%s\";\n", lease->filename);
+ if (lease->server_name)
+ fprintf(leaseFile, " server-name \"%s\";\n",
+ lease->server_name);
+ if (lease->medium)
+ fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string);
+ for (i = 0; i < 256; i++)
+ if (lease->options[i].len)
+ fprintf(leaseFile, " option %s %s;\n",
+ dhcp_options[i].name,
+ pretty_print_option(i, lease->options[i].data,
+ lease->options[i].len, 1, 1));
+
+ t = gmtime(&lease->renewal);
+ fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n",
+ t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ t = gmtime(&lease->rebind);
+ fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n",
+ t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ t = gmtime(&lease->expiry);
+ fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n",
+ t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ fprintf(leaseFile, "}\n");
+ fflush(leaseFile);
+}
+
+void
+script_init(char *reason, struct string_list *medium)
+{
+ size_t len, mediumlen = 0;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs;
+
+ if (medium != NULL && medium->string != NULL)
+ mediumlen = strlen(medium->string);
+
+ hdr.code = IMSG_SCRIPT_INIT;
+ hdr.len = sizeof(struct imsg_hdr) +
+ sizeof(size_t) + mediumlen +
+ sizeof(size_t) + strlen(reason);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, &mediumlen, sizeof(mediumlen));
+ if (mediumlen > 0)
+ errs += buf_add(buf, medium->string, mediumlen);
+ len = strlen(reason);
+ errs += buf_add(buf, &len, sizeof(len));
+ errs += buf_add(buf, reason, len);
+
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
+void
+priv_script_init(char *reason, char *medium)
+{
+ struct interface_info *ip = ifi;
+
+ if (ip) {
+ ip->client->scriptEnvsize = 100;
+ if (ip->client->scriptEnv == NULL)
+ ip->client->scriptEnv =
+ malloc(ip->client->scriptEnvsize * sizeof(char *));
+ if (ip->client->scriptEnv == NULL)
+ error("script_init: no memory for environment");
+
+ ip->client->scriptEnv[0] = strdup(CLIENT_PATH);
+ if (ip->client->scriptEnv[0] == NULL)
+ error("script_init: no memory for environment");
+
+ ip->client->scriptEnv[1] = NULL;
+
+ script_set_env(ip->client, "", "interface", ip->name);
+
+ if (medium)
+ script_set_env(ip->client, "", "medium", medium);
+
+ script_set_env(ip->client, "", "reason", reason);
+ }
+}
+
+void
+priv_script_write_params(char *prefix, struct client_lease *lease)
+{
+ struct interface_info *ip = ifi;
+ u_int8_t dbuf[1500];
+ int i, len = 0;
+ char tbuf[128];
+
+ script_set_env(ip->client, prefix, "ip_address",
+ piaddr(lease->address));
+
+ if (lease->options[DHO_SUBNET_MASK].len &&
+ (lease->options[DHO_SUBNET_MASK].len <
+ sizeof(lease->address.iabuf))) {
+ struct iaddr netmask, subnet, broadcast;
+
+ memcpy(netmask.iabuf, lease->options[DHO_SUBNET_MASK].data,
+ lease->options[DHO_SUBNET_MASK].len);
+ netmask.len = lease->options[DHO_SUBNET_MASK].len;
+
+ subnet = subnet_number(lease->address, netmask);
+ if (subnet.len) {
+ script_set_env(ip->client, prefix, "network_number",
+ piaddr(subnet));
+ if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
+ broadcast = broadcast_addr(subnet, netmask);
+ if (broadcast.len)
+ script_set_env(ip->client, prefix,
+ "broadcast_address",
+ piaddr(broadcast));
+ }
+ }
+ }
+
+ if (lease->filename)
+ script_set_env(ip->client, prefix, "filename", lease->filename);
+ if (lease->server_name)
+ script_set_env(ip->client, prefix, "server_name",
+ lease->server_name);
+ for (i = 0; i < 256; i++) {
+ u_int8_t *dp = NULL;
+
+ if (ip->client->config->defaults[i].len) {
+ if (lease->options[i].len) {
+ switch (
+ ip->client->config->default_actions[i]) {
+ case ACTION_DEFAULT:
+ dp = lease->options[i].data;
+ len = lease->options[i].len;
+ break;
+ case ACTION_SUPERSEDE:
+supersede:
+ dp = ip->client->
+ config->defaults[i].data;
+ len = ip->client->
+ config->defaults[i].len;
+ break;
+ case ACTION_PREPEND:
+ len = ip->client->
+ config->defaults[i].len +
+ lease->options[i].len;
+ if (len > sizeof(dbuf)) {
+ warning("no space to %s %s",
+ "prepend option",
+ dhcp_options[i].name);
+ goto supersede;
+ }
+ dp = dbuf;
+ memcpy(dp,
+ ip->client->
+ config->defaults[i].data,
+ ip->client->
+ config->defaults[i].len);
+ memcpy(dp + ip->client->
+ config->defaults[i].len,
+ lease->options[i].data,
+ lease->options[i].len);
+ dp[len] = '\0';
+ break;
+ case ACTION_APPEND:
+ len = ip->client->
+ config->defaults[i].len +
+ lease->options[i].len;
+ if (len > sizeof(dbuf)) {
+ warning("no space to %s %s",
+ "append option",
+ dhcp_options[i].name);
+ goto supersede;
+ }
+ dp = dbuf;
+ memcpy(dp,
+ lease->options[i].data,
+ lease->options[i].len);
+ memcpy(dp + lease->options[i].len,
+ ip->client->
+ config->defaults[i].data,
+ ip->client->
+ config->defaults[i].len);
+ dp[len] = '\0';
+ }
+ } else {
+ dp = ip->client->
+ config->defaults[i].data;
+ len = ip->client->
+ config->defaults[i].len;
+ }
+ } else if (lease->options[i].len) {
+ len = lease->options[i].len;
+ dp = lease->options[i].data;
+ } else {
+ len = 0;
+ }
+ if (len) {
+ char name[256];
+
+ if (dhcp_option_ev_name(name, sizeof(name),
+ &dhcp_options[i]))
+ script_set_env(ip->client, prefix, name,
+ pretty_print_option(i, dp, len, 0, 0));
+ }
+ }
+ snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
+ script_set_env(ip->client, prefix, "expiry", tbuf);
+}
+
+void
+script_write_params(char *prefix, struct client_lease *lease)
+{
+ size_t fn_len = 0, sn_len = 0, pr_len = 0;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs, i;
+
+ if (lease->filename != NULL)
+ fn_len = strlen(lease->filename);
+ if (lease->server_name != NULL)
+ sn_len = strlen(lease->server_name);
+ if (prefix != NULL)
+ pr_len = strlen(prefix);
+
+ hdr.code = IMSG_SCRIPT_WRITE_PARAMS;
+ hdr.len = sizeof(hdr) + sizeof(struct client_lease) +
+ sizeof(size_t) + fn_len + sizeof(size_t) + sn_len +
+ sizeof(size_t) + pr_len;
+
+ for (i = 0; i < 256; i++)
+ hdr.len += sizeof(int) + lease->options[i].len;
+
+ scripttime = time(NULL);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, lease, sizeof(struct client_lease));
+ errs += buf_add(buf, &fn_len, sizeof(fn_len));
+ errs += buf_add(buf, lease->filename, fn_len);
+ errs += buf_add(buf, &sn_len, sizeof(sn_len));
+ errs += buf_add(buf, lease->server_name, sn_len);
+ errs += buf_add(buf, &pr_len, sizeof(pr_len));
+ errs += buf_add(buf, prefix, pr_len);
+
+ for (i = 0; i < 256; i++) {
+ errs += buf_add(buf, &lease->options[i].len,
+ sizeof(lease->options[i].len));
+ errs += buf_add(buf, lease->options[i].data,
+ lease->options[i].len);
+ }
+
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
+int
+script_go(void)
+{
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int ret;
+
+ scripttime = time(NULL);
+
+ hdr.code = IMSG_SCRIPT_GO;
+ hdr.len = sizeof(struct imsg_hdr);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ if (buf_add(buf, &hdr, sizeof(hdr)))
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+
+ bzero(&hdr, sizeof(hdr));
+ buf_read(privfd, &hdr, sizeof(hdr));
+ if (hdr.code != IMSG_SCRIPT_GO_RET)
+ error("unexpected msg type %u", hdr.code);
+ if (hdr.len != sizeof(hdr) + sizeof(int))
+ error("received corrupted message");
+ buf_read(privfd, &ret, sizeof(ret));
+
+ return (ret);
+}
+
+int
+priv_script_go(void)
+{
+ char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI";
+ static char client_path[] = CLIENT_PATH;
+ struct interface_info *ip = ifi;
+ int pid, wpid, wstatus;
+
+ scripttime = time(NULL);
+
+ if (ip) {
+ scriptName = ip->client->config->script_name;
+ envp = ip->client->scriptEnv;
+ } else {
+ scriptName = top_level_config.script_name;
+ epp[0] = reason;
+ epp[1] = client_path;
+ epp[2] = NULL;
+ envp = epp;
+ }
+
+ argv[0] = scriptName;
+ argv[1] = NULL;
+
+ pid = fork();
+ if (pid < 0) {
+ error("fork: %m");
+ wstatus = 0;
+ } else if (pid) {
+ do {
+ wpid = wait(&wstatus);
+ } while (wpid != pid && wpid > 0);
+ if (wpid < 0) {
+ error("wait: %m");
+ wstatus = 0;
+ }
+ } else {
+ execve(scriptName, argv, envp);
+ error("execve (%s, ...): %m", scriptName);
+ }
+
+ if (ip)
+ script_flush_env(ip->client);
+
+ return (wstatus & 0xff);
+}
+
+void
+script_set_env(struct client_state *client, const char *prefix,
+ const char *name, const char *value)
+{
+ int i, j, namelen;
+
+ namelen = strlen(name);
+
+ for (i = 0; client->scriptEnv[i]; i++)
+ if (strncmp(client->scriptEnv[i], name, namelen) == 0 &&
+ client->scriptEnv[i][namelen] == '=')
+ break;
+
+ if (client->scriptEnv[i])
+ /* Reuse the slot. */
+ free(client->scriptEnv[i]);
+ else {
+ /* New variable. Expand if necessary. */
+ if (i >= client->scriptEnvsize - 1) {
+ char **newscriptEnv;
+ int newscriptEnvsize = client->scriptEnvsize + 50;
+
+ newscriptEnv = realloc(client->scriptEnv,
+ newscriptEnvsize);
+ if (newscriptEnv == NULL) {
+ free(client->scriptEnv);
+ client->scriptEnv = NULL;
+ client->scriptEnvsize = 0;
+ error("script_set_env: no memory for variable");
+ }
+ client->scriptEnv = newscriptEnv;
+ client->scriptEnvsize = newscriptEnvsize;
+ }
+ /* need to set the NULL pointer at end of array beyond
+ the new slot. */
+ client->scriptEnv[i + 1] = NULL;
+ }
+ /* Allocate space and format the variable in the appropriate slot. */
+ client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 +
+ strlen(value) + 1);
+ if (client->scriptEnv[i] == NULL)
+ error("script_set_env: no memory for variable assignment");
+
+ /* No `` or $() command substitution allowed in environment values! */
+ for (j=0; j < strlen(value); j++)
+ switch (value[j]) {
+ case '`':
+ case '$':
+ error("illegal character (%c) in value '%s'", value[j],
+ value);
+ /* not reached */
+ }
+ snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) +
+ 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value);
+}
+
+void
+script_flush_env(struct client_state *client)
+{
+ int i;
+
+ for (i = 0; client->scriptEnv[i]; i++) {
+ free(client->scriptEnv[i]);
+ client->scriptEnv[i] = NULL;
+ }
+ client->scriptEnvsize = 0;
+}
+
+int
+dhcp_option_ev_name(char *buf, size_t buflen, struct option *option)
+{
+ int i;
+
+ for (i = 0; option->name[i]; i++) {
+ if (i + 1 == buflen)
+ return 0;
+ if (option->name[i] == '-')
+ buf[i] = '_';
+ else
+ buf[i] = option->name[i];
+ }
+
+ buf[i] = 0;
+ return 1;
+}
+
+void
+go_daemon(void)
+{
+ static int state = 0;
+
+ if (no_daemon || state)
+ return;
+
+ state = 1;
+
+ /* Stop logging to stderr... */
+ log_perror = 0;
+
+ if (daemon(1, 0) == -1)
+ error("daemon");
+
+ /* we are chrooted, daemon(3) fails to open /dev/null */
+ if (nullfd != -1) {
+ dup2(nullfd, STDIN_FILENO);
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ close(nullfd);
+ nullfd = -1;
+ }
+}
+
+int
+check_option(struct client_lease *l, int option)
+{
+ char *opbuf;
+ char *sbuf;
+
+ /* we use this, since this is what gets passed to dhclient-script */
+
+ opbuf = pretty_print_option(option, l->options[option].data,
+ l->options[option].len, 0, 0);
+
+ sbuf = option_as_string(option, l->options[option].data,
+ l->options[option].len);
+
+ switch (option) {
+ case DHO_SUBNET_MASK:
+ case DHO_TIME_SERVERS:
+ case DHO_NAME_SERVERS:
+ case DHO_ROUTERS:
+ case DHO_DOMAIN_NAME_SERVERS:
+ case DHO_LOG_SERVERS:
+ case DHO_COOKIE_SERVERS:
+ case DHO_LPR_SERVERS:
+ case DHO_IMPRESS_SERVERS:
+ case DHO_RESOURCE_LOCATION_SERVERS:
+ case DHO_SWAP_SERVER:
+ case DHO_BROADCAST_ADDRESS:
+ case DHO_NIS_SERVERS:
+ case DHO_NTP_SERVERS:
+ case DHO_NETBIOS_NAME_SERVERS:
+ case DHO_NETBIOS_DD_SERVER:
+ case DHO_FONT_SERVERS:
+ case DHO_DHCP_SERVER_IDENTIFIER:
+ if (!ipv4addrs(opbuf)) {
+ warning("Invalid IP address in option: %s", opbuf);
+ return (0);
+ }
+ return (1) ;
+ case DHO_HOST_NAME:
+ case DHO_DOMAIN_NAME:
+ case DHO_NIS_DOMAIN:
+ if (!res_hnok(sbuf)) {
+ warning("Bogus Host Name option %d: %s (%s)", option,
+ sbuf, opbuf);
+ return (0);
+ }
+ return (1);
+ case DHO_PAD:
+ case DHO_TIME_OFFSET:
+ case DHO_BOOT_SIZE:
+ case DHO_MERIT_DUMP:
+ case DHO_ROOT_PATH:
+ case DHO_EXTENSIONS_PATH:
+ case DHO_IP_FORWARDING:
+ case DHO_NON_LOCAL_SOURCE_ROUTING:
+ case DHO_POLICY_FILTER:
+ case DHO_MAX_DGRAM_REASSEMBLY:
+ case DHO_DEFAULT_IP_TTL:
+ case DHO_PATH_MTU_AGING_TIMEOUT:
+ case DHO_PATH_MTU_PLATEAU_TABLE:
+ case DHO_INTERFACE_MTU:
+ case DHO_ALL_SUBNETS_LOCAL:
+ case DHO_PERFORM_MASK_DISCOVERY:
+ case DHO_MASK_SUPPLIER:
+ case DHO_ROUTER_DISCOVERY:
+ case DHO_ROUTER_SOLICITATION_ADDRESS:
+ case DHO_STATIC_ROUTES:
+ case DHO_TRAILER_ENCAPSULATION:
+ case DHO_ARP_CACHE_TIMEOUT:
+ case DHO_IEEE802_3_ENCAPSULATION:
+ case DHO_DEFAULT_TCP_TTL:
+ case DHO_TCP_KEEPALIVE_INTERVAL:
+ case DHO_TCP_KEEPALIVE_GARBAGE:
+ case DHO_VENDOR_ENCAPSULATED_OPTIONS:
+ case DHO_NETBIOS_NODE_TYPE:
+ case DHO_NETBIOS_SCOPE:
+ case DHO_X_DISPLAY_MANAGER:
+ case DHO_DHCP_REQUESTED_ADDRESS:
+ case DHO_DHCP_LEASE_TIME:
+ case DHO_DHCP_OPTION_OVERLOAD:
+ case DHO_DHCP_MESSAGE_TYPE:
+ case DHO_DHCP_PARAMETER_REQUEST_LIST:
+ case DHO_DHCP_MESSAGE:
+ case DHO_DHCP_MAX_MESSAGE_SIZE:
+ case DHO_DHCP_RENEWAL_TIME:
+ case DHO_DHCP_REBINDING_TIME:
+ case DHO_DHCP_CLASS_IDENTIFIER:
+ case DHO_DHCP_CLIENT_IDENTIFIER:
+ case DHO_DHCP_USER_CLASS_ID:
+ case DHO_END:
+ return (1);
+ default:
+ warning("unknown dhcp option value 0x%x", option);
+ return (unknown_ok);
+ }
+}
+
+int
+res_hnok(const char *dn)
+{
+ int pch = PERIOD, ch = *dn++;
+
+ while (ch != '\0') {
+ int nch = *dn++;
+
+ if (periodchar(ch)) {
+ ;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch))
+ return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch))
+ return (0);
+ } else {
+ if (!middlechar(ch))
+ return (0);
+ }
+ pch = ch, ch = nch;
+ }
+ return (1);
+}
+
+/* Does buf consist only of dotted decimal ipv4 addrs?
+ * return how many if so,
+ * otherwise, return 0
+ */
+int
+ipv4addrs(char * buf)
+{
+ struct in_addr jnk;
+ int count = 0;
+
+ while (inet_aton(buf, &jnk) == 1){
+ count++;
+ while (periodchar(*buf) || digitchar(*buf))
+ buf++;
+ if (*buf == '\0')
+ return (count);
+ while (*buf == ' ')
+ buf++;
+ }
+ return (0);
+}
+
+
+char *
+option_as_string(unsigned int code, unsigned char *data, int len)
+{
+ static char optbuf[32768]; /* XXX */
+ char *op = optbuf;
+ int opleft = sizeof(optbuf);
+ unsigned char *dp = data;
+
+ if (code > 255)
+ error("option_as_string: bad code %d", code);
+
+ for (; dp < data + len; dp++) {
+ if (!isascii(*dp) || !isprint(*dp)) {
+ if (dp + 1 != data + len || *dp != 0) {
+ snprintf(op, opleft, "\\%03o", *dp);
+ op += 4;
+ opleft -= 4;
+ }
+ } else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
+ *dp == '`' || *dp == '\\') {
+ *op++ = '\\';
+ *op++ = *dp;
+ opleft -= 2;
+ } else {
+ *op++ = *dp;
+ opleft--;
+ }
+ }
+ if (opleft < 1)
+ goto toobig;
+ *op = 0;
+ return optbuf;
+toobig:
+ warning("dhcp option too large");
+ return "<error>";
+}
+
+int
+fork_privchld(int fd, int fd2)
+{
+ struct pollfd pfd[1];
+ int nfds;
+
+ switch (fork()) {
+ case -1:
+ error("cannot fork");
+ case 0:
+ break;
+ default:
+ return (0);
+ }
+
+ setproctitle("%s [priv]", ifi->name);
+
+ dup2(nullfd, STDIN_FILENO);
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ close(nullfd);
+ close(fd2);
+
+ for (;;) {
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ if ((nfds = poll(pfd, 1, INFTIM)) == -1)
+ if (errno != EINTR)
+ error("poll error");
+
+ if (nfds == 0 || !(pfd[0].revents & POLLIN))
+ continue;
+
+ dispatch_imsg(fd);
+ }
+}
diff --git a/sbin/dhclient/dhclient.conf b/sbin/dhclient/dhclient.conf
new file mode 100644
index 000000000000..147e0045a5d8
--- /dev/null
+++ b/sbin/dhclient/dhclient.conf
@@ -0,0 +1,36 @@
+send host-name "andare.fugue.com";
+send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
+send dhcp-lease-time 3600;
+supersede domain-name "fugue.com home.vix.com";
+prepend domain-name-servers 127.0.0.1;
+request subnet-mask, broadcast-address, time-offset, routers,
+ domain-name, domain-name-servers, host-name;
+require subnet-mask, domain-name-servers;
+timeout 60;
+retry 60;
+reboot 10;
+select-timeout 5;
+initial-interval 2;
+script "/etc/dhclient-script";
+media "-link0 -link1 -link2", "link0 link1";
+reject 192.33.137.209;
+
+alias {
+ interface "ep0";
+ fixed-address 192.5.5.213;
+ option subnet-mask 255.255.255.255;
+}
+
+lease {
+ interface "ep0";
+ fixed-address 192.33.137.200;
+ medium "link0 link1";
+ option host-name "andare.swiftmedia.com";
+ option subnet-mask 255.255.255.0;
+ option broadcast-address 192.33.137.255;
+ option routers 192.33.137.250;
+ option domain-name-servers 127.0.0.1;
+ renew 2 2000/1/12 00:00:01;
+ rebind 2 2000/1/12 00:00:01;
+ expire 2 2000/1/12 00:00:01;
+}
diff --git a/sbin/dhclient/dhclient.conf.5 b/sbin/dhclient/dhclient.conf.5
new file mode 100644
index 000000000000..daf6f79c9537
--- /dev/null
+++ b/sbin/dhclient/dhclient.conf.5
@@ -0,0 +1,541 @@
+.\" $OpenBSD: dhclient.conf.5,v 1.5 2004/11/01 23:10:18 henning Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1997
+.Dt DHCLIENT.CONF 5
+.Os
+.Sh NAME
+.Nm dhclient.conf
+.Nd DHCP client configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file contains configuration information for
+.Xr dhclient 8 ,
+the Internet Software Consortium DHCP Client.
+.Pp
+The
+.Nm
+file is a free-form ASCII text file.
+It is parsed by the recursive-descent parser built into
+.Xr dhclient 8 .
+The file may contain extra tabs and newlines for formatting purposes.
+Keywords in the file are case-insensitive.
+Comments may be placed anywhere within the file (except within quotes).
+Comments begin with the
+.Sq #
+character and end at the end of the line.
+.Pp
+The
+.Nm
+file can be used to configure the behaviour of the client in a wide variety
+of ways: protocol timing, information requested from the server, information
+required of the server, defaults to use if the server does not provide
+certain information, values with which to override information provided by
+the server, or values to prepend or append to information provided by the
+server.
+The configuration file can also be preinitialized with addresses to
+use on networks that don't have DHCP servers.
+.Sh PROTOCOL TIMING
+The timing behaviour of the client need not be configured by the user.
+If no timing configuration is provided by the user, a fairly
+reasonable timing behaviour will be used by default \- one which
+results in fairly timely updates without placing an inordinate load on
+the server.
+.Pp
+The following statements can be used to adjust the timing behaviour of
+the DHCP client if required, however:
+.Bl -tag -width Ds
+.It Ic timeout Ar time ;
+The
+.Ic timeout
+statement determines the amount of time that must pass between the
+time that the client begins to try to determine its address and the
+time that it decides that it's not going to be able to contact a server.
+By default, this timeout is sixty seconds.
+After the timeout has passed, if there are any static leases defined in the
+configuration file, or any leases remaining in the lease database that
+have not yet expired, the client will loop through these leases
+attempting to validate them, and if it finds one that appears to be
+valid, it will use that lease's address.
+If there are no valid static leases or unexpired leases in the lease database,
+the client will restart the protocol after the defined retry interval.
+.It Ic retry Ar time ;
+The
+.Ic retry
+statement determines the time that must pass after the client has
+determined that there is no DHCP server present before it tries again
+to contact a DHCP server.
+By default, this is five minutes.
+.It Ic select-timeout Ar time ;
+It is possible (some might say desirable) for there to be more than
+one DHCP server serving any given network.
+In this case, it is possible that a client may be sent more than one offer
+in response to its initial lease discovery message.
+It may be that one of these offers is preferable to the other
+(e.g., one offer may have the address the client previously used,
+and the other may not).
+.Pp
+The
+.Ic select-timeout
+is the time after the client sends its first lease discovery request
+at which it stops waiting for offers from servers, assuming that it
+has received at least one such offer.
+If no offers have been received by the time the
+.Ic select-timeout
+has expired, the client will accept the first offer that arrives.
+.Pp
+By default, the
+.Ic select-timeout
+is zero seconds \- that is, the client will take the first offer it sees.
+.It Ic reboot Ar time ;
+When the client is restarted, it first tries to reacquire the last
+address it had.
+This is called the INIT-REBOOT state.
+If it is still attached to the same network it was attached to when it last
+ran, this is the quickest way to get started.
+The
+.Ic reboot
+statement sets the time that must elapse after the client first tries
+to reacquire its old address before it gives up and tries to discover
+a new address.
+By default, the reboot timeout is ten seconds.
+.It Ic backoff-cutoff Ar time ;
+The client uses an exponential backoff algorithm with some randomness,
+so that if many clients try to configure themselves at the same time,
+they will not make their requests in lockstep.
+The
+.Ic backoff-cutoff
+statement determines the maximum amount of time that the client is
+allowed to back off.
+It defaults to two minutes.
+.It Ic initial-interval Ar time ;
+The
+.Ic initial-interval
+statement sets the amount of time between the first attempt to reach a
+server and the second attempt to reach a server.
+Each time a message is sent, the interval between messages is incremented by
+twice the current interval multiplied by a random number between zero and one.
+If it is greater than the backoff-cutoff amount, it is set to that
+amount.
+It defaults to ten seconds.
+.El
+.Sh LEASE REQUIREMENTS AND REQUESTS
+The DHCP protocol allows the client to request that the server send it
+specific information, and not send it other information that it is not
+prepared to accept.
+The protocol also allows the client to reject offers from servers if they
+don't contain information the client needs, or if the information provided
+is not satisfactory.
+.Pp
+There is a variety of data contained in offers that DHCP servers send
+to DHCP clients.
+The data that can be specifically requested is what are called
+.Em DHCP Options .
+DHCP Options are defined in
+.Xr dhcp-options 5 .
+.Bl -tag -width Ds
+.It Xo
+.Ic request Op Ar option
+.Oo , Ar ... option Oc ;
+.Xc
+The
+.Ic request
+statement causes the client to request that any server responding to the
+client send the client its values for the specified options.
+Only the option names should be specified in the request statement \- not
+option parameters.
+.It Xo
+.Ic require Op Ar option
+.Oo , Ar ... option Oc ;
+.Xc
+The
+.Ic require
+statement lists options that must be sent in order for an offer to be accepted.
+Offers that do not contain all the listed options will be ignored.
+.It Xo
+.Ic send No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+The
+.Ic send
+statement causes the client to send the specified options to the server with
+the specified values.
+These are full option declarations as described in
+.Xr dhcp-options 5 .
+Options that are always sent in the DHCP protocol should not be specified
+here, except that the client can specify a
+.Ar dhcp-lease-time
+option other than the default requested lease time, which is two hours.
+The other obvious use for this statement is to send information to the server
+that will allow it to differentiate between this client and other
+clients or kinds of clients.
+.El
+.Sh OPTION MODIFIERS
+In some cases, a client may receive option data from the server which
+is not really appropriate for that client, or may not receive
+information that it needs, and for which a useful default value exists.
+It may also receive information which is useful, but which needs to be
+supplemented with local information.
+To handle these needs, several option modifiers are available.
+.Bl -tag -width Ds
+.It Xo
+.Ic default No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should use the value supplied by
+the server, but needs to use some default value if no value was supplied
+by the server, these values can be defined in the
+.Ic default
+statement.
+.It Xo
+.Ic supersede No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should always use its own value
+rather than any value supplied by the server, these values can be defined
+in the
+.Ic supersede
+statement.
+.It Xo
+.Ic prepend No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should use a value you supply,
+and then use the values supplied by the server, if any,
+these values can be defined in the
+.Ic prepend
+statement.
+The
+.Ic prepend
+statement can only be used for options which allow more than one value to
+be given.
+This restriction is not enforced \- if violated, the results are unpredictable.
+.It Xo
+.Ic append No { Op Ar option declaration
+.Oo , Ar ... option declaration Oc }
+.Xc
+If for some set of options the client should first use the values
+supplied by the server, if any, and then use values you supply, these
+values can be defined in the
+.Ic append
+statement.
+The
+.Ic append
+statement can only be used for options which allow more than one value to
+be given.
+This restriction is not enforced \- if you ignore it,
+the behaviour will be unpredictable.
+.El
+.Sh LEASE DECLARATIONS
+The lease declaration:
+.Pp
+.Xo
+.Ic \ \& lease No { Ar lease-declaration
+.Oo Ar ... lease-declaration Oc }
+.Xc
+.Pp
+The DHCP client may decide after some period of time (see
+.Sx PROTOCOL TIMING )
+that it is not going to succeed in contacting a server.
+At that time, it consults its own database of old leases and tests each one
+that has not yet timed out by pinging the listed router for that lease to
+see if that lease could work.
+It is possible to define one or more
+.Em fixed
+leases in the client configuration file for networks where there is no DHCP
+or BOOTP service, so that the client can still automatically configure its
+address.
+This is done with the
+.Ic lease
+statement.
+.Pp
+NOTE: the lease statement is also used in the
+.Pa dhclient.leases
+file in order to record leases that have been received from DHCP servers.
+Some of the syntax for leases as described below is only needed in the
+.Pa dhclient.leases
+file.
+Such syntax is documented here for completeness.
+.Pp
+A lease statement consists of the lease keyword, followed by a left
+curly brace, followed by one or more lease declaration statements,
+followed by a right curly brace.
+The following lease declarations are possible:
+.Bl -tag -width Ds
+.It Ic bootp ;
+The
+.Ic bootp
+statement is used to indicate that the lease was acquired using the
+BOOTP protocol rather than the DHCP protocol.
+It is never necessary to specify this in the client configuration file.
+The client uses this syntax in its lease database file.
+.It Ic interface Ar \&"string\&" ;
+The
+.Ic interface
+lease statement is used to indicate the interface on which the lease is valid.
+If set, this lease will only be tried on a particular interface.
+When the client receives a lease from a server, it always records the
+interface number on which it received that lease.
+If predefined leases are specified in the
+.Nm
+file, the interface should also be specified, although this is not required.
+.It Ic fixed-address Ar ip-address ;
+The
+.Ic fixed-address
+statement is used to set the IP address of a particular lease.
+This is required for all lease statements.
+The IP address must be specified as a dotted quad (e.g., 12.34.56.78).
+.It Ic filename Ar \&"string\&" ;
+The
+.Ic filename
+statement specifies the name of the boot filename to use.
+This is not used by the standard client configuration script, but is
+included for completeness.
+.It Ic server-name Ar \&"string\&" ;
+The
+.Ic server-name
+statement specifies the name of the boot server name to use.
+This is also not used by the standard client configuration script.
+.It Ic option Ar option-declaration ;
+The
+.Ic option
+statement is used to specify the value of an option supplied by the server,
+or, in the case of predefined leases declared in
+.Nm dhclient.conf ,
+the value that the user wishes the client configuration script to use if the
+predefined lease is used.
+.It Ic script Ar \&"script-name\&" ;
+The
+.Ic script
+statement is used to specify the pathname of the DHCP client configuration
+script.
+This script is used by the DHCP client to set each interface's initial
+configuration prior to requesting an address, to test the address once it
+has been offered, and to set the interface's final configuration once a
+lease has been acquired.
+If no lease is acquired, the script is used to test predefined leases, if
+any, and also called once if no valid lease can be identified.
+For more information, see
+.Xr dhclient.leases 5 .
+.It Ic medium Ar \&"media setup\&" ;
+The
+.Ic medium
+statement can be used on systems where network interfaces cannot
+automatically determine the type of network to which they are connected.
+The media setup string is a system-dependent parameter which is passed
+to the DHCP client configuration script when initializing the interface.
+On
+.Ux
+and UNIX-like systems, the argument is passed on the ifconfig command line
+when configuring the interface.
+.Pp
+The DHCP client automatically declares this parameter if it used a
+media type (see the
+.Ic media
+statement) when configuring the interface in order to obtain a lease.
+This statement should be used in predefined leases only if the network
+interface requires media type configuration.
+.It Ic renew Ar date ;
+.It Ic rebind Ar date ;
+.It Ic expire Ar date ;
+The
+.Ic renew
+statement defines the time at which the DHCP client should begin trying to
+contact its server to renew a lease that it is using.
+The
+.Ic rebind
+statement defines the time at which the DHCP client should begin to try to
+contact
+.Em any
+DHCP server in order to renew its lease.
+The
+.Ic expire
+statement defines the time at which the DHCP client must stop using a lease
+if it has not been able to contact a server in order to renew it.
+.El
+.Pp
+These declarations are automatically set in leases acquired by the
+DHCP client, but must also be configured in predefined leases \- a
+predefined lease whose expiry time has passed will not be used by the
+DHCP client.
+.Pp
+Dates are specified as follows:
+.Pp
+.Ar \ \&<weekday>
+.Sm off
+.Ar <year> No / Ar <month> No / Ar <day>
+.Ar <hour> : <minute> : <second>
+.Sm on
+.Pp
+The weekday is present to make it easy for a human to tell when a
+lease expires \- it's specified as a number from zero to six, with zero
+being Sunday.
+When declaring a predefined lease, it can always be specified as zero.
+The year is specified with the century, so it should generally be four
+digits except for really long leases.
+The month is specified as a number starting with 1 for January.
+The day of the month is likewise specified starting with 1.
+The hour is a number between 0 and 23,
+the minute a number between 0 and 59,
+and the second also a number between 0 and 59.
+.Sh ALIAS DECLARATIONS
+.Ic alias No { Ar declarations ... No }
+.Pp
+Some DHCP clients running TCP/IP roaming protocols may require that in
+addition to the lease they may acquire via DHCP, their interface also
+be configured with a predefined IP alias so that they can have a
+permanent IP address even while roaming.
+The Internet Software Consortium DHCP client doesn't support roaming with
+fixed addresses directly, but in order to facilitate such experimentation,
+the DHCP client can be set up to configure an IP alias using the
+.Ic alias
+declaration.
+.Pp
+The
+.Ic alias
+declaration resembles a lease declaration, except that options other than
+the subnet-mask option are ignored by the standard client configuration
+script, and expiry times are ignored.
+A typical alias declaration includes an interface declaration, a fixed-address
+declaration for the IP alias address, and a subnet-mask option declaration.
+A medium statement should never be included in an alias declaration.
+.Sh OTHER DECLARATIONS
+.Bl -tag -width Ds
+.It Ic reject Ar ip-address ;
+The
+.Ic reject
+statement causes the DHCP client to reject offers from servers who use
+the specified address as a server identifier.
+This can be used to avoid being configured by rogue or misconfigured DHCP
+servers, although it should be a last resort \- better to track down
+the bad DHCP server and fix it.
+.It Xo
+.Ic interface Ar \&"name\&" No { Ar declarations
+.Ar ... No }
+.Xc
+A client with more than one network interface may require different
+behaviour depending on which interface is being configured.
+All timing parameters and declarations other than lease and alias
+declarations can be enclosed in an interface declaration, and those
+parameters will then be used only for the interface that matches the
+specified name.
+Interfaces for which there is no interface declaration will use the
+parameters declared outside of any interface declaration,
+or the default settings.
+.It Xo
+.Ic media Ar \&"media setup\&"
+.Oo , Ar \&"media setup\&" , ... Oc ;
+.Xc
+The
+.Ic media
+statement defines one or more media configuration parameters which may
+be tried while attempting to acquire an IP address.
+The DHCP client will cycle through each media setup string on the list,
+configuring the interface using that setup and attempting to boot,
+and then trying the next one.
+This can be used for network interfaces which aren't capable of sensing
+the media type unaided \- whichever media type succeeds in getting a request
+to the server and hearing the reply is probably right (no guarantees).
+.Pp
+The media setup is only used for the initial phase of address
+acquisition (the DHCPDISCOVER and DHCPOFFER packets).
+Once an address has been acquired, the DHCP client will record it in its
+lease database and will record the media type used to acquire the address.
+Whenever the client tries to renew the lease, it will use that same media type.
+The lease must expire before the client will go back to cycling through media
+types.
+.El
+.Sh EXAMPLES
+The following configuration file is used on a laptop
+which has an IP alias of 192.5.5.213, and has one interface,
+ep0 (a 3Com 3C589C).
+Booting intervals have been shortened somewhat from the default, because
+the client is known to spend most of its time on networks with little DHCP
+activity.
+The laptop does roam to multiple networks.
+.Bd -literal -offset indent
+timeout 60;
+retry 60;
+reboot 10;
+select-timeout 5;
+initial-interval 2;
+reject 192.33.137.209;
+
+interface "ep0" {
+ send host-name "andare.fugue.com";
+ send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
+ send dhcp-lease-time 3600;
+ supersede domain-name "fugue.com rc.vix.com home.vix.com";
+ prepend domain-name-servers 127.0.0.1;
+ request subnet-mask, broadcast-address, time-offset, routers,
+ domain-name, domain-name-servers, host-name;
+ require subnet-mask, domain-name-servers;
+ script "/etc/dhclient-script";
+ media "media 10baseT/UTP", "media 10base2/BNC";
+}
+
+alias {
+ interface "ep0";
+ fixed-address 192.5.5.213;
+ option subnet-mask 255.255.255.255;
+}
+.Ed
+.Pp
+This is a very complicated
+.Nm
+file \- in general, yours should be much simpler.
+In many cases, it's sufficient to just create an empty
+.Nm
+file \- the defaults are usually fine.
+.Sh SEE ALSO
+.Xr dhclient.leases 5 ,
+.Xr dhcp-options 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Pp
+RFC 2132, RFC 2131.
+.Sh AUTHORS
+.Xr dhclient 8
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhclient.leases.5 b/sbin/dhclient/dhclient.leases.5
new file mode 100644
index 000000000000..d5a8a2958767
--- /dev/null
+++ b/sbin/dhclient/dhclient.leases.5
@@ -0,0 +1,87 @@
+.\" $OpenBSD: dhclient.leases.5,v 1.4 2004/04/15 08:59:47 jmc Exp $
+.\"
+.\" Copyright (c) 1997 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1997
+.Dt DHCLIENT.LEASES 5
+.Os
+.Sh NAME
+.Nm dhclient.leases
+.Nd DHCP client lease database
+.Sh DESCRIPTION
+The Internet Software Consortium DHCP client keeps a persistent
+database of leases that it has acquired that are still valid.
+The database is a free-form ASCII file containing one valid declaration
+per lease.
+If more than one declaration appears for a given lease,
+the last one in the file is used.
+The file is written as a log, so this is not an unusual occurrence.
+.Pp
+The lease file is named
+.Qq dhclient.leases.IFNAME ,
+where
+.Qq IFNAME
+represents the network interface the DHCP client acquired the lease on.
+For example, if
+.Xr dhclient 8
+is configured for the em0 network device,
+the lease file will be named
+.Qq dhclient.leases.em0 .
+.Pp
+The format of the lease declarations is described in
+.Xr dhclient.conf 5 .
+.Sh FILES
+.Bl -tag -width "/var/db/dhclient.leases.IFNAME "
+.It /var/db/dhclient.leases.IFNAME
+Current lease file.
+.El
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhcp-options 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Pp
+RFC 2132, RFC 2131.
+.Sh AUTHORS
+.Xr dhclient 8
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhcp-options.5 b/sbin/dhclient/dhcp-options.5
new file mode 100644
index 000000000000..da3dd1c3c079
--- /dev/null
+++ b/sbin/dhclient/dhcp-options.5
@@ -0,0 +1,590 @@
+.\" $OpenBSD: dhcp-options.5,v 1.5 2005/03/02 15:30:42 jmc Exp $
+.\"
+.\" Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+.\" Enterprises. To learn more about the Internet Software Consortium,
+.\" see ``http://www.isc.org/isc''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.Dd January 1, 1995
+.Dt DHCP-OPTIONS 5
+.Os
+.Sh NAME
+.Nm dhcp-options
+.Nd Dynamic Host Configuration Protocol options
+.Sh DESCRIPTION
+The Dynamic Host Configuration protocol allows the client to receive
+.Ic options
+from the DHCP server describing the network configuration and various
+services that are available on the network.
+When configuring
+.Xr dhcpd 8
+or
+.Xr dhclient 8 ,
+options must often be declared.
+The syntax for declaring options, and the names and formats of the options
+that can be declared, are documented here.
+.Sh REFERENCE: OPTION STATEMENTS
+DHCP
+.Ic option
+statements always start with the
+.Ic option
+keyword, followed by an option name, followed by option data.
+The option names and data formats are described below.
+It is not necessary to exhaustively specify all DHCP options \-
+only those options which are needed by clients must be specified.
+.Pp
+Option data comes in a variety of formats, as defined below:
+.Pp
+The
+.Ar ip-address
+data type can be entered either as an explicit IP address
+(e.g., 239.254.197.10) or as a domain name (e.g., haagen.isc.org).
+A domain name must resolve to a single IP address.
+.Pp
+The
+.Ar int32
+data type specifies a signed 32-bit integer.
+The
+.Ar uint32
+data type specifies an unsigned 32-bit integer.
+The
+.Ar int16
+and
+.Ar uint16
+data types specify signed and unsigned 16-bit integers.
+The
+.Ar int8
+and
+.Ar uint8
+data types specify signed and unsigned 8-bit integers.
+Unsigned 8-bit integers are also sometimes referred to as octets.
+.Pp
+The
+.Ar string
+data type specifies an
+.Tn NVT
+.Pq Network Virtual Terminal
+.Tn ASCII
+string, which must be enclosed in double quotes \- for example,
+to specify a domain-name option, the syntax would be
+.Pp
+.Dl option domain-name \&"isc.org\&";
+.Pp
+The
+.Ar flag
+data type specifies a boolean value.
+Booleans can be either true or false
+(or on or off, if that makes more sense to you).
+.Pp
+The
+.Ar data-string
+data type specifies either an
+.Tn NVT ASCII
+string enclosed in double quotes, or a series of octets specified in
+hexadecimal, separated by colons.
+For example:
+.Pp
+.Dl option dhcp-client-identifier \&"CLIENT-FOO\&";
+or
+.Dl option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f;
+.Pp
+The documentation for the various options mentioned below is taken
+from the IETF draft document on DHCP options, RFC 2132.
+Options which are not listed by name may be defined by the name
+.Pf option\- Ns Ar nnn ,
+where
+.Ar nnn
+is the decimal number of the option code.
+These options may be followed either by a string, enclosed in quotes, or by
+a series of octets, expressed as two-digit hexadecimal numbers separated
+by colons.
+For example:
+.Bd -literal -offset indent
+option option-133 "my-option-133-text";
+option option-129 1:54:c9:2b:47;
+.Ed
+.Pp
+Because
+.Xr dhcpd 8
+does not know the format of these undefined option codes,
+no checking is done to ensure the correctness of the entered data.
+.Pp
+The standard options are:
+.Ss RFC 1497 Vendor Extensions
+.Bl -tag -width Ds
+.It Ic option subnet-mask Ar ip-address ;
+The
+.Ic subnet-mask
+option specifies the client's subnet mask as per RFC 950.
+If no subnet-mask option is provided anywhere in scope, as a last resort
+.Xr dhcpd 8
+will use the subnet mask from the subnet declaration for the network on
+which an address is being assigned.
+However,
+.Em any
+subnet-mask option declaration that is in scope for the address being
+assigned will override the subnet mask specified in the subnet declaration.
+.It Ic option time-offset Ar int32 ;
+The
+.Ic time-offset
+option specifies the offset of the client's subnet in seconds from
+Coordinated Universal Time (UTC).
+.It Xo
+.Ic option routers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic routers
+option specifies a list of IP addresses for routers on the client's subnet.
+Routers should be listed in order of preference.
+.It Xo
+.Ic option time-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic time-server
+option specifies a list of RFC 868 time servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option ien116-name-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic ien116-name-servers
+option specifies a list of IEN 116 name servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option domain-name-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic domain-name-servers
+option specifies a list of Domain Name System (STD 13, RFC 1035) name servers
+available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option log-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic log-servers
+option specifies a list of MIT-LCS UDP log servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option cookie-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic cookie-servers
+option specifies a list of RFC 865 cookie servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option lpr-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic lpr-servers
+option specifies a list of RFC 1179 line printer servers available to the
+client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option impress-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic impress-servers
+option specifies a list of Imagen Impress servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option resource-location-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of RFC 887 Resource Location servers available
+to the client.
+Servers should be listed in order of preference.
+.It Ic option host-name Ar string ;
+This option specifies the name of the client.
+The name may or may not be qualified with the local domain name
+(it is preferable to use the
+.Ic domain-name
+option to specify the domain name).
+See RFC 1035 for character set restrictions.
+.It Ic option boot-size Ar uint16 ;
+This option specifies the length in 512-octet blocks of the default
+boot image for the client.
+.It Ic option merit-dump Ar string ;
+This option specifies the pathname of a file to which the client's
+core image should be dumped in the event the client crashes.
+The path is formatted as a character string consisting of characters from
+the
+.Tn NVT ASCII
+character set.
+.It Ic option domain-name Ar string ;
+This option specifies the domain name that the client should use when
+resolving hostnames via the Domain Name System.
+.It Ic option swap-server Ar ip-address ;
+This specifies the IP address of the client's swap server.
+.It Ic option root-path Ar string ;
+This option specifies the pathname that contains the client's root disk.
+The path is formatted as a character string consisting of characters from
+the
+.Tn NVT ASCII
+character set.
+.El
+.Ss IP Layer Parameters per Host
+.Bl -tag -width Ds
+.It Ic option ip-forwarding Ar flag ;
+This option specifies whether the client should configure its IP layer
+for packet forwarding.
+A value of 0 means disable IP forwarding, and a value of 1 means enable
+IP forwarding.
+.It Ic option non-local-source-routing Ar flag ;
+This option specifies whether the client should configure its IP
+layer to allow forwarding of datagrams with non-local source routes
+(see Section 3.3.5 of [4] for a discussion of this topic).
+A value of 0 means disallow forwarding of such datagrams, and a value of 1
+means allow forwarding.
+.It Xo
+.Ic option policy-filter Ar ip-address ip-address
+.Oo , Ar ip-address ip-address ... Oc ;
+.Xc
+This option specifies policy filters for non-local source routing.
+The filters consist of a list of IP addresses and masks which specify
+destination/mask pairs with which to filter incoming source routes.
+.Pp
+Any source-routed datagram whose next-hop address does not match one
+of the filters should be discarded by the client.
+.Pp
+See STD 3 (RFC 1122) for further information.
+.It Ic option max-dgram-reassembly Ar uint16 ;
+This option specifies the maximum size datagram that the client should be
+prepared to reassemble.
+The minimum legal value is 576.
+.It Ic option default-ip-ttl Ar uint8 ;
+This option specifies the default time-to-live that the client should
+use on outgoing datagrams.
+.It Ic option path-mtu-aging-timeout Ar uint32 ;
+This option specifies the timeout (in seconds) to use when aging Path
+MTU values discovered by the mechanism defined in RFC 1191.
+.It Xo
+.Ic option path-mtu-plateau-table Ar uint16
+.Oo , Ar uint16 ... Oc ;
+.Xc
+This option specifies a table of MTU sizes to use when performing
+Path MTU Discovery as defined in RFC 1191.
+The table is formatted as a list of 16-bit unsigned integers,
+ordered from smallest to largest.
+The minimum MTU value cannot be smaller than 68.
+.El
+.Ss IP Layer Parameters per Interface
+.Bl -tag -width Ds
+.It Ic option interface-mtu Ar uint16 ;
+This option specifies the MTU to use on this interface.
+The minimum legal value for the MTU is 68.
+.It Ic option all-subnets-local Ar flag ;
+This option specifies whether or not the client may assume that all subnets
+of the IP network to which the client is connected use the same MTU as the
+subnet of that network to which the client is directly connected.
+A value of 1 indicates that all subnets share the same MTU.
+A value of 0 means that the client should assume that some subnets of the
+directly connected network may have smaller MTUs.
+.It Ic option broadcast-address Ar ip-address ;
+This option specifies the broadcast address in use on the client's subnet.
+Legal values for broadcast addresses are specified in section 3.2.1.3 of
+STD 3 (RFC 1122).
+.It Ic option perform-mask-discovery Ar flag ;
+This option specifies whether or not the client should perform subnet mask
+discovery using ICMP.
+A value of 0 indicates that the client should not perform mask discovery.
+A value of 1 means that the client should perform mask discovery.
+.It Ic option mask-supplier Ar flag ;
+This option specifies whether or not the client should respond to subnet mask
+requests using ICMP.
+A value of 0 indicates that the client should not respond.
+A value of 1 means that the client should respond.
+.It Ic option router-discovery Ar flag ;
+This option specifies whether or not the client should solicit routers using
+the Router Discovery mechanism defined in RFC 1256.
+A value of 0 indicates that the client should not perform router discovery.
+A value of 1 means that the client should perform router discovery.
+.It Ic option router-solicitation-address Ar ip-address ;
+This option specifies the address to which the client should transmit
+router solicitation requests.
+.It Xo
+.Ic option static-routes Ar ip-address ip-address
+.Oo , Ar ip-address ip-address ... Oc ;
+.Xc
+This option specifies a list of static routes that the client should
+install in its routing cache.
+If multiple routes to the same destination are specified, they are listed
+in descending order of priority.
+.Pp
+The routes consist of a list of IP address pairs.
+The first address is the destination address,
+and the second address is the router for the destination.
+.Pp
+The default route (0.0.0.0) is an illegal destination for a static route.
+To specify the default route, use the
+.Ic routers
+option.
+.El
+.Ss Link Layer Parameters per Interface
+.Bl -tag -width Ds
+.It Ic option trailer-encapsulation Ar flag ;
+This option specifies whether or not the client should negotiate the
+use of trailers (RFC 893 [14]) when using the ARP protocol.
+A value of 0 indicates that the client should not attempt to use trailers.
+A value of 1 means that the client should attempt to use trailers.
+.It Ic option arp-cache-timeout Ar uint32 ;
+This option specifies the timeout in seconds for ARP cache entries.
+.It Ic option ieee802-3-encapsulation Ar flag ;
+This option specifies whether or not the client should use Ethernet
+Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the
+interface is an Ethernet.
+A value of 0 indicates that the client should use RFC 894 encapsulation.
+A value of 1 means that the client should use RFC 1042 encapsulation.
+.El
+.Ss TCP Parameters
+.Bl -tag -width Ds
+.It Ic option default-tcp-ttl Ar uint8 ;
+This option specifies the default TTL that the client should use when
+sending TCP segments.
+The minimum value is 1.
+.It Ic option tcp-keepalive-interval Ar uint32 ;
+This option specifies the interval (in seconds) that the client TCP
+should wait before sending a keepalive message on a TCP connection.
+The time is specified as a 32-bit unsigned integer.
+A value of zero indicates that the client should not generate keepalive
+messages on connections unless specifically requested by an application.
+.It Ic option tcp-keepalive-garbage Ar flag ;
+This option specifies whether or not the client should send TCP keepalive
+messages with an octet of garbage for compatibility with older implementations.
+A value of 0 indicates that a garbage octet should not be sent.
+A value of 1 indicates that a garbage octet should be sent.
+.El
+.Ss Application and Service Parameters
+.Bl -tag -width Ds
+.It Ic option nis-domain Ar string ;
+This option specifies the name of the client's NIS (Sun Network Information
+Services) domain.
+The domain is formatted as a character string consisting of characters
+from the
+.Tn NVT ASCII
+character set.
+.It Xo
+.Ic option nis-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating NIS servers
+available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option ntp-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating NTP (RFC 1035)
+servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option netbios-name-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The NetBIOS name server (NBNS) option specifies a list of RFC 1001/1002
+NBNS name servers listed in order of preference.
+NetBIOS Name Service is currently more commonly referred to as WINS.
+WINS servers can be specified using the
+.Ic netbios-name-servers
+option.
+.It Xo
+.Ic option netbios-dd-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The NetBIOS datagram distribution server (NBDD) option specifies a
+list of RFC 1001/1002 NBDD servers listed in order of preference.
+.It Ic option netbios-node-type Ar uint8 ;
+The NetBIOS node type option allows NetBIOS over TCP/IP clients which
+are configurable to be configured as described in RFC 1001/1002.
+The value is specified as a single octet which identifies the client type.
+.Pp
+Possible node types are:
+.Bl -tag -width Ds
+.It 1
+B-node: Broadcast - no WINS
+.It 2
+P-node: Peer - WINS only
+.It 4
+M-node: Mixed - broadcast, then WINS
+.It 8
+H-node: Hybrid - WINS, then broadcast
+.El
+.It Ic option netbios-scope Ar string ;
+The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
+parameter for the client as specified in RFC 1001/1002.
+See RFC 1001, RFC 1002, and RFC 1035 for character-set restrictions.
+.It Xo
+.Ic option font-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of X Window System Font servers available
+to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option x-display-manager Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of systems that are running the X Window
+System Display Manager and are available to the client.
+Addresses should be listed in order of preference.
+.It Ic option dhcp-client-identifier Ar data-string ;
+This option can be used to specify a DHCP client identifier in a
+host declaration, so that
+.Xr dhcpd 8
+can find the host record by matching against the client identifier.
+.It Ic option nisplus-domain Ar string ;
+This option specifies the name of the client's NIS+ domain.
+The domain is formatted as a character string consisting of characters
+from the
+.Tn NVT ASCII
+character set.
+.It Xo
+.Ic option nisplus-servers Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating NIS+ servers
+available to the client.
+Servers should be listed in order of preference.
+.It Ic option tftp-server-name Ar string ;
+This option is used to identify a TFTP server and, if supported by the
+client, should have the same effect as the
+.Ic server-name
+declaration.
+BOOTP clients are unlikely to support this option.
+Some DHCP clients will support it, and others actually require it.
+.It Ic option bootfile-name Ar string ;
+This option is used to identify a bootstrap file.
+If supported by the client, it should have the same effect as the
+.Ic filename
+declaration.
+BOOTP clients are unlikely to support this option.
+Some DHCP clients will support it, and others actually require it.
+.It Xo
+.Ic option mobile-ip-home-agent Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+This option specifies a list of IP addresses indicating mobile IP
+home agents available to the client.
+Agents should be listed in order of preference, although normally there
+will be only one such agent.
+.It Xo
+.Ic option smtp-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic smtp-server
+option specifies a list of SMTP servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option pop-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic pop-server
+option specifies a list of POP3 servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option nntp-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic nntp-server
+option specifies a list of NNTP servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option www-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic www-server
+option specifies a list of WWW servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option finger-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic finger-server
+option specifies a list of
+.Xr finger 1
+servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option irc-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic irc-server
+option specifies a list of IRC servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option streettalk-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The
+.Ic streettalk-server
+option specifies a list of StreetTalk servers available to the client.
+Servers should be listed in order of preference.
+.It Xo
+.Ic option streettalk-directory-assistance-server Ar ip-address
+.Oo , Ar ip-address ... Oc ;
+.Xc
+The StreetTalk Directory Assistance (STDA) server option specifies a
+list of STDA servers available to the client.
+Servers should be listed in order of preference.
+.El
+.Sh SEE ALSO
+.Xr dhclient.conf 5 ,
+.Xr dhcpd.conf 5 ,
+.Xr dhcpd.leases 5 ,
+.Xr dhclient 8 ,
+.Xr dhcpd 8
+.Pp
+RFC 2131, RFC 2132.
+.Sh AUTHORS
+.Xr dhcpd 8
+was written by
+.An Ted Lemon Aq mellon@vix.com
+under a contract with Vixie Labs.
+.Pp
+The current implementation was reworked by
+.An Henning Brauer Aq henning@openbsd.org .
diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h
new file mode 100644
index 000000000000..33f51224a378
--- /dev/null
+++ b/sbin/dhclient/dhcp.h
@@ -0,0 +1,168 @@
+/* $OpenBSD: dhcp.h,v 1.5 2004/05/04 15:49:49 deraadt Exp $ */
+
+/* Protocol structures... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \
+ 20 + /* IP header */ \
+ 8) /* UDP header */
+#define DHCP_SNAME_LEN 64
+#define DHCP_FILE_LEN 128
+#define DHCP_FIXED_NON_UDP 236
+#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD)
+ /* Everything but options. */
+#define DHCP_MTU_MAX 1500
+#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN)
+
+#define BOOTP_MIN_LEN 300
+#define DHCP_MIN_LEN 548
+
+struct dhcp_packet {
+ u_int8_t op; /* Message opcode/type */
+ u_int8_t htype; /* Hardware addr type (see net/if_types.h) */
+ u_int8_t hlen; /* Hardware addr length */
+ u_int8_t hops; /* Number of relay agent hops from client */
+ u_int32_t xid; /* Transaction ID */
+ u_int16_t secs; /* Seconds since client started looking */
+ u_int16_t flags; /* Flag bits */
+ struct in_addr ciaddr; /* Client IP address (if already in use) */
+ struct in_addr yiaddr; /* Client IP address */
+ struct in_addr siaddr; /* IP address of next server to talk to */
+ struct in_addr giaddr; /* DHCP relay agent IP address */
+ unsigned char chaddr[16]; /* Client hardware address */
+ char sname[DHCP_SNAME_LEN]; /* Server name */
+ char file[DHCP_FILE_LEN]; /* Boot filename */
+ unsigned char options[DHCP_OPTION_LEN];
+ /* Optional parameters
+ (actual length dependent on MTU). */
+};
+
+/* BOOTP (rfc951) message types */
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+/* Possible values for flags field... */
+#define BOOTP_BROADCAST 32768L
+
+/* Possible values for hardware type (htype) field... */
+#define HTYPE_ETHER 1 /* Ethernet */
+#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */
+#define HTYPE_FDDI 8 /* FDDI... */
+
+/* Magic cookie validating dhcp options field (and bootp vendor
+ extensions field). */
+#define DHCP_OPTIONS_COOKIE "\143\202\123\143"
+
+/* DHCP Option codes: */
+
+#define DHO_PAD 0
+#define DHO_SUBNET_MASK 1
+#define DHO_TIME_OFFSET 2
+#define DHO_ROUTERS 3
+#define DHO_TIME_SERVERS 4
+#define DHO_NAME_SERVERS 5
+#define DHO_DOMAIN_NAME_SERVERS 6
+#define DHO_LOG_SERVERS 7
+#define DHO_COOKIE_SERVERS 8
+#define DHO_LPR_SERVERS 9
+#define DHO_IMPRESS_SERVERS 10
+#define DHO_RESOURCE_LOCATION_SERVERS 11
+#define DHO_HOST_NAME 12
+#define DHO_BOOT_SIZE 13
+#define DHO_MERIT_DUMP 14
+#define DHO_DOMAIN_NAME 15
+#define DHO_SWAP_SERVER 16
+#define DHO_ROOT_PATH 17
+#define DHO_EXTENSIONS_PATH 18
+#define DHO_IP_FORWARDING 19
+#define DHO_NON_LOCAL_SOURCE_ROUTING 20
+#define DHO_POLICY_FILTER 21
+#define DHO_MAX_DGRAM_REASSEMBLY 22
+#define DHO_DEFAULT_IP_TTL 23
+#define DHO_PATH_MTU_AGING_TIMEOUT 24
+#define DHO_PATH_MTU_PLATEAU_TABLE 25
+#define DHO_INTERFACE_MTU 26
+#define DHO_ALL_SUBNETS_LOCAL 27
+#define DHO_BROADCAST_ADDRESS 28
+#define DHO_PERFORM_MASK_DISCOVERY 29
+#define DHO_MASK_SUPPLIER 30
+#define DHO_ROUTER_DISCOVERY 31
+#define DHO_ROUTER_SOLICITATION_ADDRESS 32
+#define DHO_STATIC_ROUTES 33
+#define DHO_TRAILER_ENCAPSULATION 34
+#define DHO_ARP_CACHE_TIMEOUT 35
+#define DHO_IEEE802_3_ENCAPSULATION 36
+#define DHO_DEFAULT_TCP_TTL 37
+#define DHO_TCP_KEEPALIVE_INTERVAL 38
+#define DHO_TCP_KEEPALIVE_GARBAGE 39
+#define DHO_NIS_DOMAIN 40
+#define DHO_NIS_SERVERS 41
+#define DHO_NTP_SERVERS 42
+#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43
+#define DHO_NETBIOS_NAME_SERVERS 44
+#define DHO_NETBIOS_DD_SERVER 45
+#define DHO_NETBIOS_NODE_TYPE 46
+#define DHO_NETBIOS_SCOPE 47
+#define DHO_FONT_SERVERS 48
+#define DHO_X_DISPLAY_MANAGER 49
+#define DHO_DHCP_REQUESTED_ADDRESS 50
+#define DHO_DHCP_LEASE_TIME 51
+#define DHO_DHCP_OPTION_OVERLOAD 52
+#define DHO_DHCP_MESSAGE_TYPE 53
+#define DHO_DHCP_SERVER_IDENTIFIER 54
+#define DHO_DHCP_PARAMETER_REQUEST_LIST 55
+#define DHO_DHCP_MESSAGE 56
+#define DHO_DHCP_MAX_MESSAGE_SIZE 57
+#define DHO_DHCP_RENEWAL_TIME 58
+#define DHO_DHCP_REBINDING_TIME 59
+#define DHO_DHCP_CLASS_IDENTIFIER 60
+#define DHO_DHCP_CLIENT_IDENTIFIER 61
+#define DHO_DHCP_USER_CLASS_ID 77
+#define DHO_END 255
+
+/* DHCP message types. */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
new file mode 100644
index 000000000000..8097f14b1de1
--- /dev/null
+++ b/sbin/dhclient/dhcpd.h
@@ -0,0 +1,437 @@
+/* $OpenBSD: dhcpd.h,v 1.33 2004/05/06 22:29:15 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "dhcp.h"
+#include "tree.h"
+
+#define LOCAL_PORT 68
+#define REMOTE_PORT 67
+
+struct option_data {
+ int len;
+ u_int8_t *data;
+};
+
+struct string_list {
+ struct string_list *next;
+ char *string;
+};
+
+struct iaddr {
+ int len;
+ unsigned char iabuf[16];
+};
+
+struct iaddrlist {
+ struct iaddrlist *next;
+ struct iaddr addr;
+};
+
+struct packet {
+ struct dhcp_packet *raw;
+ int packet_length;
+ int packet_type;
+ int options_valid;
+ int client_port;
+ struct iaddr client_addr;
+ struct interface_info *interface;
+ struct hardware *haddr;
+ struct option_data options[256];
+};
+
+struct hardware {
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t haddr[16];
+};
+
+struct client_lease {
+ struct client_lease *next;
+ time_t expiry, renewal, rebind;
+ struct iaddr address;
+ char *server_name;
+ char *filename;
+ struct string_list *medium;
+ unsigned int is_static : 1;
+ unsigned int is_bootp : 1;
+ struct option_data options[256];
+};
+
+/* Possible states in which the client can be. */
+enum dhcp_state {
+ S_REBOOTING,
+ S_INIT,
+ S_SELECTING,
+ S_REQUESTING,
+ S_BOUND,
+ S_RENEWING,
+ S_REBINDING
+};
+
+struct client_config {
+ struct option_data defaults[256];
+ enum {
+ ACTION_DEFAULT,
+ ACTION_SUPERSEDE,
+ ACTION_PREPEND,
+ ACTION_APPEND
+ } default_actions[256];
+
+ struct option_data send_options[256];
+ u_int8_t required_options[256];
+ u_int8_t requested_options[256];
+ int requested_option_count;
+ time_t timeout;
+ time_t initial_interval;
+ time_t retry_interval;
+ time_t select_interval;
+ time_t reboot_timeout;
+ time_t backoff_cutoff;
+ struct string_list *media;
+ char *script_name;
+ enum { IGNORE, ACCEPT, PREFER }
+ bootp_policy;
+ struct string_list *medium;
+ struct iaddrlist *reject_list;
+};
+
+struct client_state {
+ struct client_lease *active;
+ struct client_lease *new;
+ struct client_lease *offered_leases;
+ struct client_lease *leases;
+ struct client_lease *alias;
+ enum dhcp_state state;
+ struct iaddr destination;
+ u_int32_t xid;
+ u_int16_t secs;
+ time_t first_sending;
+ time_t interval;
+ struct string_list *medium;
+ struct dhcp_packet packet;
+ int packet_length;
+ struct iaddr requested_address;
+ struct client_config *config;
+ char **scriptEnv;
+ int scriptEnvsize;
+ struct string_list *env;
+ int envc;
+};
+
+struct interface_info {
+ struct interface_info *next;
+ struct hardware hw_address;
+ struct in_addr primary_address;
+ char name[IFNAMSIZ];
+ int rfdesc;
+ int wfdesc;
+ unsigned char *rbuf;
+ size_t rbuf_max;
+ size_t rbuf_offset;
+ size_t rbuf_len;
+ struct ifreq *ifp;
+ struct client_state *client;
+ int noifmedia;
+ int errors;
+ int dead;
+ u_int16_t index;
+};
+
+struct timeout {
+ struct timeout *next;
+ time_t when;
+ void (*func)(void *);
+ void *what;
+};
+
+struct protocol {
+ struct protocol *next;
+ int fd;
+ void (*handler)(struct protocol *);
+ void *local;
+};
+
+#define DEFAULT_HASH_SIZE 97
+
+struct hash_bucket {
+ struct hash_bucket *next;
+ unsigned char *name;
+ int len;
+ unsigned char *value;
+};
+
+struct hash_table {
+ int hash_count;
+ struct hash_bucket *buckets[DEFAULT_HASH_SIZE];
+};
+
+/* Default path to dhcpd config file. */
+#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf"
+#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
+#define DHCPD_LOG_FACILITY LOG_DAEMON
+
+#define MAX_TIME 0x7fffffff
+#define MIN_TIME 0
+
+/* External definitions... */
+
+/* options.c */
+int cons_options(struct packet *, struct dhcp_packet *, int,
+ struct tree_cache **, int, int, int, u_int8_t *, int);
+char *pretty_print_option(unsigned int,
+ unsigned char *, int, int, int);
+void do_packet(struct interface_info *, struct dhcp_packet *,
+ int, unsigned int, struct iaddr, struct hardware *);
+
+/* errwarn.c */
+extern int warnings_occurred;
+void error(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int warning(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int note(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int debug(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+int parse_warn(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+
+/* conflex.c */
+extern int lexline, lexchar;
+extern char *token_line, *tlname;
+extern char comments[4096];
+extern int comment_index;
+extern int eol_token;
+void new_parse(char *);
+int next_token(char **, FILE *);
+int peek_token(char **, FILE *);
+
+/* parse.c */
+void skip_to_semi(FILE *);
+int parse_semi(FILE *);
+char *parse_string(FILE *);
+int parse_ip_addr(FILE *, struct iaddr *);
+void parse_hardware_param(FILE *, struct hardware *);
+void parse_lease_time(FILE *, time_t *);
+unsigned char *parse_numeric_aggregate(FILE *, unsigned char *, int *,
+ int, int, int);
+void convert_num(unsigned char *, char *, int, int);
+time_t parse_date(FILE *);
+
+/* tree.c */
+pair cons(caddr_t, pair);
+
+/* alloc.c */
+struct string_list *new_string_list(size_t size);
+struct hash_table *new_hash_table(int);
+struct hash_bucket *new_hash_bucket(void);
+
+/* bpf.c */
+int if_register_bpf(struct interface_info *);
+void if_register_send(struct interface_info *);
+void if_register_receive(struct interface_info *);
+ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t,
+ struct in_addr, struct sockaddr_in *, struct hardware *);
+ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+
+/* dispatch.c */
+extern void (*bootp_packet_handler)(struct interface_info *,
+ struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *);
+void discover_interfaces(struct interface_info *);
+void reinitialize_interfaces(void);
+void dispatch(void);
+void got_one(struct protocol *);
+void add_timeout(time_t, void (*)(void *), void *);
+void cancel_timeout(void (*)(void *), void *);
+void add_protocol(char *, int, void (*)(struct protocol *), void *);
+void remove_protocol(struct protocol *);
+int interface_link_status(char *);
+
+/* hash.c */
+struct hash_table *new_hash(void);
+void add_hash(struct hash_table *, unsigned char *, int, unsigned char *);
+unsigned char *hash_lookup(struct hash_table *, unsigned char *, int);
+
+/* tables.c */
+extern struct option dhcp_options[256];
+extern unsigned char dhcp_option_default_priority_list[];
+extern int sizeof_dhcp_option_default_priority_list;
+extern struct hash_table universe_hash;
+extern struct universe dhcp_universe;
+void initialize_universes(void);
+
+/* convert.c */
+u_int32_t getULong(unsigned char *);
+int32_t getLong(unsigned char *);
+u_int16_t getUShort(unsigned char *);
+int16_t getShort(unsigned char *);
+void putULong(unsigned char *, u_int32_t);
+void putLong(unsigned char *, int32_t);
+void putUShort(unsigned char *, unsigned int);
+void putShort(unsigned char *, int);
+
+/* inet.c */
+struct iaddr subnet_number(struct iaddr, struct iaddr);
+struct iaddr broadcast_addr(struct iaddr, struct iaddr);
+int addr_eq(struct iaddr, struct iaddr);
+char *piaddr(struct iaddr);
+
+/* dhclient.c */
+extern char *path_dhclient_conf;
+extern char *path_dhclient_db;
+extern time_t cur_time;
+extern int log_priority;
+extern int log_perror;
+
+extern struct client_config top_level_config;
+
+void dhcpoffer(struct packet *);
+void dhcpack(struct packet *);
+void dhcpnak(struct packet *);
+
+void send_discover(void *);
+void send_request(void *);
+void send_decline(void *);
+
+void state_reboot(void *);
+void state_init(void *);
+void state_selecting(void *);
+void state_requesting(void *);
+void state_bound(void *);
+void state_panic(void *);
+
+void bind_lease(struct interface_info *);
+
+void make_discover(struct interface_info *, struct client_lease *);
+void make_request(struct interface_info *, struct client_lease *);
+void make_decline(struct interface_info *, struct client_lease *);
+
+void free_client_lease(struct client_lease *);
+void rewrite_client_leases(void);
+void write_client_lease(struct interface_info *, struct client_lease *, int);
+
+void priv_script_init(char *, char *);
+void priv_script_write_params(char *, struct client_lease *);
+int priv_script_go(void);
+
+void script_init(char *, struct string_list *);
+void script_write_params(char *, struct client_lease *);
+int script_go(void);
+void client_envadd(struct client_state *,
+ const char *, const char *, const char *, ...);
+void script_set_env(struct client_state *, const char *, const char *,
+ const char *);
+void script_flush_env(struct client_state *);
+int dhcp_option_ev_name(char *, size_t, struct option *);
+
+struct client_lease *packet_to_lease(struct packet *);
+void go_daemon(void);
+void client_location_changed(void);
+
+void bootp(struct packet *);
+void dhcp(struct packet *);
+
+/* packet.c */
+void assemble_hw_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+void assemble_udp_ip_header(unsigned char *, int *, u_int32_t, u_int32_t,
+ unsigned int, unsigned char *, int);
+ssize_t decode_hw_header(unsigned char *, int, struct hardware *);
+ssize_t decode_udp_ip_header(unsigned char *, int, struct sockaddr_in *,
+ unsigned char *, int);
+
+/* ethernet.c */
+void assemble_ethernet_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
+ int, struct hardware *);
+
+/* clparse.c */
+int read_client_conf(void);
+void read_client_leases(void);
+void parse_client_statement(FILE *, struct interface_info *,
+ struct client_config *);
+int parse_X(FILE *, u_int8_t *, int);
+int parse_option_list(FILE *, u_int8_t *);
+void parse_interface_declaration(FILE *, struct client_config *);
+struct interface_info *interface_or_dummy(char *);
+void make_client_state(struct interface_info *);
+void make_client_config(struct interface_info *, struct client_config *);
+void parse_client_lease_statement(FILE *, int);
+void parse_client_lease_declaration(FILE *, struct client_lease *,
+ struct interface_info **);
+struct option *parse_option_decl(FILE *, struct option_data *);
+void parse_string_list(FILE *, struct string_list **, int);
+void parse_reject_statement(FILE *, struct client_config *);
+
+/* privsep.c */
+struct buf *buf_open(size_t);
+int buf_add(struct buf *, void *, size_t);
+int buf_close(int, struct buf *);
+ssize_t buf_read(int, void *, size_t);
+void dispatch_imsg(int);
diff --git a/sbin/dhclient/dhctoken.h b/sbin/dhclient/dhctoken.h
new file mode 100644
index 000000000000..7b23242fbac0
--- /dev/null
+++ b/sbin/dhclient/dhctoken.h
@@ -0,0 +1,136 @@
+/* $OpenBSD: dhctoken.h,v 1.2 2004/02/04 12:16:56 henning Exp $ */
+
+/* Tokens for config file lexer and parser. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#define SEMI ';'
+#define DOT '.'
+#define COLON ':'
+#define COMMA ','
+#define SLASH '/'
+#define LBRACE '{'
+#define RBRACE '}'
+
+#define FIRST_TOKEN HOST
+#define HOST 256
+#define HARDWARE 257
+#define FILENAME 258
+#define FIXED_ADDR 259
+#define OPTION 260
+#define ETHERNET 261
+#define STRING 262
+#define NUMBER 263
+#define NUMBER_OR_NAME 264
+#define NAME 265
+#define TIMESTAMP 266
+#define STARTS 267
+#define ENDS 268
+#define UID 269
+#define CLASS 270
+#define LEASE 271
+#define RANGE 272
+#define PACKET 273
+#define CIADDR 274
+#define YIADDR 275
+#define SIADDR 276
+#define GIADDR 277
+#define SUBNET 278
+#define NETMASK 279
+#define DEFAULT_LEASE_TIME 280
+#define MAX_LEASE_TIME 281
+#define VENDOR_CLASS 282
+#define USER_CLASS 283
+#define SHARED_NETWORK 284
+#define SERVER_NAME 285
+#define DYNAMIC_BOOTP 286
+#define SERVER_IDENTIFIER 287
+#define DYNAMIC_BOOTP_LEASE_CUTOFF 288
+#define DYNAMIC_BOOTP_LEASE_LENGTH 289
+#define BOOT_UNKNOWN_CLIENTS 290
+#define NEXT_SERVER 291
+#define TOKEN_RING 292
+#define GROUP 293
+#define ONE_LEASE_PER_CLIENT 294
+#define GET_LEASE_HOSTNAMES 295
+#define USE_HOST_DECL_NAMES 296
+#define SEND 297
+#define CLIENT_IDENTIFIER 298
+#define REQUEST 299
+#define REQUIRE 300
+#define TIMEOUT 301
+#define RETRY 302
+#define SELECT_TIMEOUT 303
+#define SCRIPT 304
+#define INTERFACE 305
+#define RENEW 306
+#define REBIND 307
+#define EXPIRE 308
+#define UNKNOWN_CLIENTS 309
+#define ALLOW 310
+#define BOOTP 311
+#define DENY 312
+#define BOOTING 313
+#define DEFAULT 314
+#define MEDIA 315
+#define MEDIUM 316
+#define ALIAS 317
+#define REBOOT 318
+#define ABANDONED 319
+#define BACKOFF_CUTOFF 320
+#define INITIAL_INTERVAL 321
+#define NAMESERVER 322
+#define DOMAIN 323
+#define SEARCH 324
+#define SUPERSEDE 325
+#define APPEND 326
+#define PREPEND 327
+#define HOSTNAME 328
+#define CLIENT_HOSTNAME 329
+#define REJECT 330
+#define FDDI 331
+#define USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 332
+#define AUTHORITATIVE 333
+#define TOKEN_NOT 334
+#define ALWAYS_REPLY_RFC1048 335
+
+#define is_identifier(x) ((x) >= FIRST_TOKEN && \
+ (x) != STRING && \
+ (x) != NUMBER && \
+ (x) != EOF)
diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c
new file mode 100644
index 000000000000..a0d7e90e865a
--- /dev/null
+++ b/sbin/dhclient/dispatch.c
@@ -0,0 +1,495 @@
+/* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $ */
+
+/*
+ * Copyright 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999
+ * The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <sys/ioctl.h>
+
+#include <net/if_media.h>
+#include <ifaddrs.h>
+#include <poll.h>
+
+struct protocol *protocols;
+struct timeout *timeouts;
+static struct timeout *free_timeouts;
+static int interfaces_invalidated;
+void (*bootp_packet_handler)(struct interface_info *,
+ struct dhcp_packet *, int, unsigned int,
+ struct iaddr, struct hardware *);
+
+static int interface_status(struct interface_info *ifinfo);
+
+/*
+ * Use getifaddrs() to get a list of all the attached interfaces. For
+ * each interface that's of type INET and not the loopback interface,
+ * register that interface with the network I/O software, figure out
+ * what subnet it's on, and add it to the list of interfaces.
+ */
+void
+discover_interfaces(struct interface_info *iface)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_in foo;
+ struct ifreq *tif;
+
+ if (getifaddrs(&ifap) != 0)
+ error("getifaddrs failed");
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_LOOPBACK) ||
+ (ifa->ifa_flags & IFF_POINTOPOINT) ||
+ (!(ifa->ifa_flags & IFF_UP)))
+ continue;
+
+ if (strcmp(iface->name, ifa->ifa_name))
+ continue;
+
+ /*
+ * If we have the capability, extract link information
+ * and record it in a linked list.
+ */
+ if (ifa->ifa_addr->sa_family == AF_LINK) {
+ struct sockaddr_dl *foo =
+ (struct sockaddr_dl *)ifa->ifa_addr;
+
+ iface->index = foo->sdl_index;
+ iface->hw_address.hlen = foo->sdl_alen;
+ iface->hw_address.htype = HTYPE_ETHER; /* XXX */
+ memcpy(iface->hw_address.haddr,
+ LLADDR(foo), foo->sdl_alen);
+ } else if (ifa->ifa_addr->sa_family == AF_INET) {
+ struct iaddr addr;
+
+ memcpy(&foo, ifa->ifa_addr, sizeof(foo));
+ if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK))
+ continue;
+ if (!iface->ifp) {
+ int len = IFNAMSIZ + ifa->ifa_addr->sa_len;
+ if ((tif = malloc(len)) == NULL)
+ error("no space to remember ifp");
+ strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
+ memcpy(&tif->ifr_addr, ifa->ifa_addr,
+ ifa->ifa_addr->sa_len);
+ iface->ifp = tif;
+ iface->primary_address = foo.sin_addr;
+ }
+ addr.len = 4;
+ memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);
+ }
+ }
+
+ if (!iface->ifp)
+ error("%s: not found", iface->name);
+
+ /* Register the interface... */
+ if_register_receive(iface);
+ if_register_send(iface);
+ add_protocol(iface->name, iface->rfdesc, got_one, iface);
+ freeifaddrs(ifap);
+}
+
+void
+reinitialize_interfaces(void)
+{
+ interfaces_invalidated = 1;
+}
+
+/*
+ * Wait for packets to come in using poll(). When a packet comes in,
+ * call receive_packet to receive the packet and possibly strip hardware
+ * addressing information from it, and then call through the
+ * bootp_packet_handler hook to try to do something with it.
+ */
+void
+dispatch(void)
+{
+ int count, i, to_msec, nfds = 0;
+ struct protocol *l;
+ struct pollfd *fds;
+ time_t howlong;
+
+ for (l = protocols; l; l = l->next)
+ nfds++;
+
+ fds = malloc(nfds * sizeof(struct pollfd));
+ if (fds == NULL)
+ error("Can't allocate poll structures.");
+
+ do {
+ /*
+ * Call any expired timeouts, and then if there's still
+ * a timeout registered, time out the select call then.
+ */
+another:
+ if (timeouts) {
+ struct timeout *t;
+
+ if (timeouts->when <= cur_time) {
+ t = timeouts;
+ timeouts = timeouts->next;
+ (*(t->func))(t->what);
+ t->next = free_timeouts;
+ free_timeouts = t;
+ goto another;
+ }
+
+ /*
+ * Figure timeout in milliseconds, and check for
+ * potential overflow, so we can cram into an
+ * int for poll, while not polling with a
+ * negative timeout and blocking indefinitely.
+ */
+ howlong = timeouts->when - cur_time;
+ if (howlong > INT_MAX / 1000)
+ howlong = INT_MAX / 1000;
+ to_msec = howlong * 1000;
+ } else
+ to_msec = -1;
+
+ /* Set up the descriptors to be polled. */
+ for (i = 0, l = protocols; l; l = l->next) {
+ struct interface_info *ip = l->local;
+
+ if (ip && (l->handler != got_one || !ip->dead)) {
+ fds[i].fd = l->fd;
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+ i++;
+ }
+ }
+
+ if (i == 0)
+ error("No live interfaces to poll on - exiting.");
+
+ /* Wait for a packet or a timeout... XXX */
+ count = poll(fds, nfds, to_msec);
+
+ /* Not likely to be transitory... */
+ if (count == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ time(&cur_time);
+ continue;
+ } else
+ error("poll: %m");
+ }
+
+ /* Get the current time... */
+ time(&cur_time);
+
+ i = 0;
+ for (l = protocols; l; l = l->next) {
+ struct interface_info *ip;
+ ip = l->local;
+ if ((fds[i].revents & (POLLIN | POLLHUP))) {
+ fds[i].revents = 0;
+ if (ip && (l->handler != got_one ||
+ !ip->dead))
+ (*(l->handler))(l);
+ if (interfaces_invalidated)
+ break;
+ }
+ i++;
+ }
+ interfaces_invalidated = 0;
+ } while (1);
+}
+
+
+void
+got_one(struct protocol *l)
+{
+ struct sockaddr_in from;
+ struct hardware hfrom;
+ struct iaddr ifrom;
+ ssize_t result;
+ union {
+ /*
+ * Packet input buffer. Must be as large as largest
+ * possible MTU.
+ */
+ unsigned char packbuf[4095];
+ struct dhcp_packet packet;
+ } u;
+ struct interface_info *ip = l->local;
+
+ if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
+ &hfrom)) == -1) {
+ warning("receive_packet failed on %s: %s", ip->name,
+ strerror(errno));
+ ip->errors++;
+ if ((!interface_status(ip)) ||
+ (ip->noifmedia && ip->errors > 20)) {
+ /* our interface has gone away. */
+ warning("Interface %s no longer appears valid.",
+ ip->name);
+ ip->dead = 1;
+ interfaces_invalidated = 1;
+ close(l->fd);
+ remove_protocol(l);
+ free(ip);
+ }
+ return;
+ }
+ if (result == 0)
+ return;
+
+ if (bootp_packet_handler) {
+ ifrom.len = 4;
+ memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
+
+ (*bootp_packet_handler)(ip, &u.packet, result,
+ from.sin_port, ifrom, &hfrom);
+ }
+}
+
+int
+interface_status(struct interface_info *ifinfo)
+{
+ char *ifname = ifinfo->name;
+ int ifsock = ifinfo->rfdesc;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname);
+ goto inactive;
+ }
+
+ /*
+ * if one of UP and RUNNING flags is dropped,
+ * the interface is not active.
+ */
+ if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ goto inactive;
+
+ /* Next, check carrier on the interface, if possible */
+ if (ifinfo->noifmedia)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
+ ifname);
+
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ /*
+ * EINVAL (or ENOTTY) simply means that the interface
+ * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->noifmedia = 1;
+ goto active;
+ }
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch (ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+inactive:
+ return (0);
+active:
+ return (1);
+}
+
+void
+add_timeout(time_t when, void (*where)(void *), void *what)
+{
+ struct timeout *t, *q;
+
+ /* See if this timeout supersedes an existing timeout. */
+ t = NULL;
+ for (q = timeouts; q; q = q->next) {
+ if (q->func == where && q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we didn't supersede a timeout, allocate a timeout
+ structure now. */
+ if (!q) {
+ if (free_timeouts) {
+ q = free_timeouts;
+ free_timeouts = q->next;
+ q->func = where;
+ q->what = what;
+ } else {
+ q = malloc(sizeof(struct timeout));
+ if (!q)
+ error("Can't allocate timeout structure!");
+ q->func = where;
+ q->what = what;
+ }
+ }
+
+ q->when = when;
+
+ /* Now sort this timeout into the timeout list. */
+
+ /* Beginning of list? */
+ if (!timeouts || timeouts->when > q->when) {
+ q->next = timeouts;
+ timeouts = q;
+ return;
+ }
+
+ /* Middle of list? */
+ for (t = timeouts; t->next; t = t->next) {
+ if (t->next->when > q->when) {
+ q->next = t->next;
+ t->next = q;
+ return;
+ }
+ }
+
+ /* End of list. */
+ t->next = q;
+ q->next = NULL;
+}
+
+void
+cancel_timeout(void (*where)(void *), void *what)
+{
+ struct timeout *t, *q;
+
+ /* Look for this timeout on the list, and unlink it if we find it. */
+ t = NULL;
+ for (q = timeouts; q; q = q->next) {
+ if (q->func == where && q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we found the timeout, put it on the free list. */
+ if (q) {
+ q->next = free_timeouts;
+ free_timeouts = q;
+ }
+}
+
+/* Add a protocol to the list of protocols... */
+void
+add_protocol(char *name, int fd, void (*handler)(struct protocol *),
+ void *local)
+{
+ struct protocol *p;
+
+ p = malloc(sizeof(*p));
+ if (!p)
+ error("can't allocate protocol struct for %s", name);
+
+ p->fd = fd;
+ p->handler = handler;
+ p->local = local;
+ p->next = protocols;
+ protocols = p;
+}
+
+void
+remove_protocol(struct protocol *proto)
+{
+ struct protocol *p, *next, *prev;
+
+ prev = NULL;
+ for (p = protocols; p; p = next) {
+ next = p->next;
+ if (p == proto) {
+ if (prev)
+ prev->next = p->next;
+ else
+ protocols = p->next;
+ free(p);
+ }
+ }
+}
+
+int
+interface_link_status(char *ifname)
+{
+ struct ifmediareq ifmr;
+ int sock;
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ error("Can't create socket");
+
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
+ /* EINVAL -> link state unknown. treat as active */
+ if (errno != EINVAL)
+ syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m",
+ ifname);
+ close(sock);
+ return (1);
+ }
+ close(sock);
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) {
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ return (1);
+ else
+ return (0);
+ }
+ }
+ return (1);
+}
diff --git a/sbin/dhclient/errwarn.c b/sbin/dhclient/errwarn.c
new file mode 100644
index 000000000000..03b75478457c
--- /dev/null
+++ b/sbin/dhclient/errwarn.c
@@ -0,0 +1,234 @@
+/* $OpenBSD: errwarn.c,v 1.7 2004/05/04 22:23:01 mickey Exp $ */
+
+/* Errors and warnings... */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium.
+ * All Rights Reserved.
+ * Copyright (c) 1995 RadioMail Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of RadioMail Corporation, the Internet Software
+ * Consortium nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION, THE INTERNET
+ * SOFTWARE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL RADIOMAIL CORPORATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for the Internet Software Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#include <errno.h>
+
+#include "dhcpd.h"
+
+static void do_percentm(char *obuf, size_t size, char *ibuf);
+
+static char mbuf[1024];
+static char fbuf[1024];
+
+int warnings_occurred;
+
+/*
+ * Log an error message, then exit.
+ */
+void
+error(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ /* Also log it to stderr? */
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ syslog(LOG_CRIT, "exiting.");
+ if (log_perror) {
+ fprintf(stderr, "exiting.\n");
+ fflush(stderr);
+ }
+ exit(1);
+}
+
+/*
+ * Log a warning message...
+ */
+int
+warning(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a note...
+ */
+int
+note(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_INFO, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Log a debug message...
+ */
+int
+debug(char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm(fbuf, sizeof(fbuf), fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_DEBUG, "%s", mbuf);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Find %m in the input string and substitute an error message string.
+ */
+static void
+do_percentm(char *obuf, size_t size, char *ibuf)
+{
+ char ch;
+ char *s = ibuf;
+ char *t = obuf;
+ size_t prlen;
+ size_t fmt_left;
+ int saved_errno = errno;
+
+ /*
+ * We wouldn't need this mess if printf handled %m, or if
+ * strerror() had been invented before syslog().
+ */
+ for (fmt_left = size; (ch = *s); ++s) {
+ if (ch == '%' && s[1] == 'm') {
+ ++s;
+ prlen = snprintf(t, fmt_left, "%s",
+ strerror(saved_errno));
+ if (prlen >= fmt_left)
+ prlen = fmt_left - 1;
+ t += prlen;
+ fmt_left -= prlen;
+ } else {
+ if (fmt_left > 1) {
+ *t++ = ch;
+ fmt_left--;
+ }
+ }
+ }
+ *t = '\0';
+}
+
+int
+parse_warn(char *fmt, ...)
+{
+ va_list list;
+ static char spaces[] =
+ " "
+ " "; /* 80 spaces */
+
+ do_percentm(mbuf, sizeof(mbuf), fmt);
+ snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+#ifndef DEBUG
+ syslog(log_priority | LOG_ERR, "%s", mbuf);
+ syslog(log_priority | LOG_ERR, "%s", token_line);
+ if (lexline < 81)
+ syslog(log_priority | LOG_ERR,
+ "%s^", &spaces[sizeof(spaces) - lexchar]);
+#endif
+
+ if (log_perror) {
+ write(2, mbuf, strlen(mbuf));
+ write(2, "\n", 1);
+ write(2, token_line, strlen(token_line));
+ write(2, "\n", 1);
+ write(2, spaces, lexchar - 1);
+ write(2, "^\n", 2);
+ }
+
+ warnings_occurred = 1;
+
+ return (0);
+}
diff --git a/sbin/dhclient/hash.c b/sbin/dhclient/hash.c
new file mode 100644
index 000000000000..4f1795b24351
--- /dev/null
+++ b/sbin/dhclient/hash.c
@@ -0,0 +1,119 @@
+/* $OpenBSD: hash.c,v 1.9 2004/05/10 15:30:47 deraadt Exp $ */
+
+/* Routines for manipulating hash tables... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+static int do_hash(unsigned char *, int, int);
+
+struct hash_table *
+new_hash(void)
+{
+ struct hash_table *rv = new_hash_table(DEFAULT_HASH_SIZE);
+
+ if (!rv)
+ return (rv);
+ memset(&rv->buckets[0], 0,
+ DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *));
+ return (rv);
+}
+
+static int
+do_hash(unsigned char *name, int len, int size)
+{
+ unsigned char *s = name;
+ int accum = 0, i = len;
+
+ while (i--) {
+ /* Add the character in... */
+ accum += *s++;
+ /* Add carry back in... */
+ while (accum > 255)
+ accum = (accum & 255) + (accum >> 8);
+ }
+ return (accum % size);
+}
+
+void add_hash(struct hash_table *table, unsigned char *name, int len,
+ unsigned char *pointer)
+{
+ struct hash_bucket *bp;
+ int hashno;
+
+ if (!table)
+ return;
+ if (!len)
+ len = strlen((char *)name);
+
+ hashno = do_hash(name, len, table->hash_count);
+ bp = new_hash_bucket();
+
+ if (!bp) {
+ warning("Can't add %s to hash table.", name);
+ return;
+ }
+ bp->name = name;
+ bp->value = pointer;
+ bp->next = table->buckets[hashno];
+ bp->len = len;
+ table->buckets[hashno] = bp;
+}
+
+unsigned char *
+hash_lookup(struct hash_table *table, unsigned char *name, int len)
+{
+ struct hash_bucket *bp;
+ int hashno;
+
+ if (!table)
+ return (NULL);
+
+ if (!len)
+ len = strlen((char *)name);
+
+ hashno = do_hash(name, len, table->hash_count);
+
+ for (bp = table->buckets[hashno]; bp; bp = bp->next)
+ if (len == bp->len && !memcmp(bp->name, name, len))
+ return (bp->value);
+
+ return (NULL);
+}
diff --git a/sbin/dhclient/inet.c b/sbin/dhclient/inet.c
new file mode 100644
index 000000000000..a52847596e12
--- /dev/null
+++ b/sbin/dhclient/inet.c
@@ -0,0 +1,118 @@
+/* $OpenBSD: inet.c,v 1.7 2004/05/04 21:48:16 deraadt Exp $ */
+
+/*
+ * Subroutines to manipulate internet addresses in a safely portable
+ * way...
+ */
+
+/*
+ * Copyright (c) 1996 The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+/*
+ * Return just the network number of an internet address...
+ */
+struct iaddr
+subnet_number(struct iaddr addr, struct iaddr mask)
+{
+ struct iaddr rv;
+ int i;
+
+ rv.len = 0;
+
+ /* Both addresses must have the same length... */
+ if (addr.len != mask.len)
+ return (rv);
+
+ rv.len = addr.len;
+ for (i = 0; i < rv.len; i++)
+ rv.iabuf[i] = addr.iabuf[i] & mask.iabuf[i];
+ return (rv);
+}
+
+/*
+ * Given a subnet number and netmask, return the address on that subnet
+ * for which the host portion of the address is all ones (the standard
+ * broadcast address).
+ */
+struct iaddr
+broadcast_addr(struct iaddr subnet, struct iaddr mask)
+{
+ struct iaddr rv;
+ int i;
+
+ if (subnet.len != mask.len) {
+ rv.len = 0;
+ return (rv);
+ }
+
+ for (i = 0; i < subnet.len; i++)
+ rv.iabuf[i] = subnet.iabuf[i] | (~mask.iabuf[i] & 255);
+ rv.len = subnet.len;
+
+ return (rv);
+}
+
+int
+addr_eq(struct iaddr addr1, struct iaddr addr2)
+{
+ if (addr1.len != addr2.len)
+ return (0);
+ return (memcmp(addr1.iabuf, addr2.iabuf, addr1.len) == 0);
+}
+
+char *
+piaddr(struct iaddr addr)
+{
+ static char pbuf[32];
+ struct in_addr a;
+ char *s;
+
+ memcpy(&a, &(addr.iabuf), sizeof(struct in_addr));
+
+ if (addr.len == 0)
+ strlcpy(pbuf, "<null address>", sizeof(pbuf));
+ else {
+ s = inet_ntoa(a);
+ if (s != NULL)
+ strlcpy(pbuf, s, sizeof(pbuf));
+ else
+ strlcpy(pbuf, "<invalid address>", sizeof(pbuf));
+ }
+ return (pbuf);
+}
diff --git a/sbin/dhclient/options.c b/sbin/dhclient/options.c
new file mode 100644
index 000000000000..9c8e8fc743f1
--- /dev/null
+++ b/sbin/dhclient/options.c
@@ -0,0 +1,717 @@
+/* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */
+
+/* DHCP options parsing and reassembly. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include <ctype.h>
+
+#define DHCP_OPTION_DATA
+#include "dhcpd.h"
+
+int bad_options = 0;
+int bad_options_max = 5;
+
+void parse_options(struct packet *);
+void parse_option_buffer(struct packet *, unsigned char *, int);
+int store_options(unsigned char *, int, struct tree_cache **,
+ unsigned char *, int, int, int, int);
+
+
+/*
+ * Parse all available options out of the specified packet.
+ */
+void
+parse_options(struct packet *packet)
+{
+ /* Initially, zero all option pointers. */
+ memset(packet->options, 0, sizeof(packet->options));
+
+ /* If we don't see the magic cookie, there's nothing to parse. */
+ if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
+ packet->options_valid = 0;
+ return;
+ }
+
+ /*
+ * Go through the options field, up to the end of the packet or
+ * the End field.
+ */
+ parse_option_buffer(packet, &packet->raw->options[4],
+ packet->packet_length - DHCP_FIXED_NON_UDP - 4);
+
+ /*
+ * If we parsed a DHCP Option Overload option, parse more
+ * options out of the buffer(s) containing them.
+ */
+ if (packet->options_valid &&
+ packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
+ if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
+ parse_option_buffer(packet,
+ (unsigned char *)packet->raw->file,
+ sizeof(packet->raw->file));
+ if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
+ parse_option_buffer(packet,
+ (unsigned char *)packet->raw->sname,
+ sizeof(packet->raw->sname));
+ }
+}
+
+/*
+ * Parse options out of the specified buffer, storing addresses of
+ * option values in packet->options and setting packet->options_valid if
+ * no errors are encountered.
+ */
+void
+parse_option_buffer(struct packet *packet,
+ unsigned char *buffer, int length)
+{
+ unsigned char *s, *t, *end = buffer + length;
+ int len, code;
+
+ for (s = buffer; *s != DHO_END && s < end; ) {
+ code = s[0];
+
+ /* Pad options don't have a length - just skip them. */
+ if (code == DHO_PAD) {
+ s++;
+ continue;
+ }
+ if (s + 2 > end) {
+ len = 65536;
+ goto bogus;
+ }
+
+ /*
+ * All other fields (except end, see above) have a
+ * one-byte length.
+ */
+ len = s[1];
+
+ /*
+ * If the length is outrageous, silently skip the rest,
+ * and mark the packet bad. Unfortunately some crappy
+ * dhcp servers always seem to give us garbage on the
+ * end of a packet. so rather than keep refusing, give
+ * up and try to take one after seeing a few without
+ * anything good.
+ */
+ if (s + len + 2 > end) {
+ bogus:
+ bad_options++;
+ warning("option %s (%d) %s.",
+ dhcp_options[code].name, len,
+ "larger than buffer");
+ if (bad_options == bad_options_max) {
+ packet->options_valid = 1;
+ bad_options = 0;
+ warning("Many bogus options seen in offers. "
+ "Taking this offer in spite of bogus "
+ "options - hope for the best!");
+ } else {
+ warning("rejecting bogus offer.");
+ packet->options_valid = 0;
+ }
+ return;
+ }
+ /*
+ * If we haven't seen this option before, just make
+ * space for it and copy it there.
+ */
+ if (!packet->options[code].data) {
+ if (!(t = calloc(1, len + 1)))
+ error("Can't allocate storage for option %s.",
+ dhcp_options[code].name);
+ /*
+ * Copy and NUL-terminate the option (in case
+ * it's an ASCII string.
+ */
+ memcpy(t, &s[2], len);
+ t[len] = 0;
+ packet->options[code].len = len;
+ packet->options[code].data = t;
+ } else {
+ /*
+ * If it's a repeat, concatenate it to whatever
+ * we last saw. This is really only required
+ * for clients, but what the heck...
+ */
+ t = calloc(1, len + packet->options[code].len + 1);
+ if (!t)
+ error("Can't expand storage for option %s.",
+ dhcp_options[code].name);
+ memcpy(t, packet->options[code].data,
+ packet->options[code].len);
+ memcpy(t + packet->options[code].len,
+ &s[2], len);
+ packet->options[code].len += len;
+ t[packet->options[code].len] = 0;
+ free(packet->options[code].data);
+ packet->options[code].data = t;
+ }
+ s += len + 2;
+ }
+ packet->options_valid = 1;
+}
+
+/*
+ * cons options into a big buffer, and then split them out into the
+ * three separate buffers if needed. This allows us to cons up a set of
+ * vendor options using the same routine.
+ */
+int
+cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
+ int mms, struct tree_cache **options,
+ int overload, /* Overload flags that may be set. */
+ int terminate, int bootpp, u_int8_t *prl, int prl_len)
+{
+ unsigned char priority_list[300], buffer[4096];
+ int priority_len, main_buffer_size, mainbufix, bufix;
+ int option_size, length;
+
+ /*
+ * If the client has provided a maximum DHCP message size, use
+ * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
+ * up to the minimum IP MTU size (576 bytes).
+ *
+ * XXX if a BOOTP client specifies a max message size, we will
+ * honor it.
+ */
+ if (!mms &&
+ inpacket &&
+ inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
+ (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
+ sizeof(u_int16_t)))
+ mms = getUShort(
+ inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
+
+ if (mms)
+ main_buffer_size = mms - DHCP_FIXED_LEN;
+ else if (bootpp)
+ main_buffer_size = 64;
+ else
+ main_buffer_size = 576 - DHCP_FIXED_LEN;
+
+ if (main_buffer_size > sizeof(buffer))
+ main_buffer_size = sizeof(buffer);
+
+ /* Preload the option priority list with mandatory options. */
+ priority_len = 0;
+ priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
+ priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
+ priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
+ priority_list[priority_len++] = DHO_DHCP_MESSAGE;
+
+ /*
+ * If the client has provided a list of options that it wishes
+ * returned, use it to prioritize. Otherwise, prioritize based
+ * on the default priority list.
+ */
+ if (inpacket &&
+ inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
+ int prlen =
+ inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
+ if (prlen + priority_len > sizeof(priority_list))
+ prlen = sizeof(priority_list) - priority_len;
+
+ memcpy(&priority_list[priority_len],
+ inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
+ prlen);
+ priority_len += prlen;
+ prl = priority_list;
+ } else if (prl) {
+ if (prl_len + priority_len > sizeof(priority_list))
+ prl_len = sizeof(priority_list) - priority_len;
+
+ memcpy(&priority_list[priority_len], prl, prl_len);
+ priority_len += prl_len;
+ prl = priority_list;
+ } else {
+ memcpy(&priority_list[priority_len],
+ dhcp_option_default_priority_list,
+ sizeof_dhcp_option_default_priority_list);
+ priority_len += sizeof_dhcp_option_default_priority_list;
+ }
+
+ /* Copy the options into the big buffer... */
+ option_size = store_options(
+ buffer,
+ (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
+ ((overload & 2) ? DHCP_SNAME_LEN : 0)),
+ options, priority_list, priority_len, main_buffer_size,
+ (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
+ terminate);
+
+ /* Put the cookie up front... */
+ memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
+ mainbufix = 4;
+
+ /*
+ * If we're going to have to overload, store the overload option
+ * at the beginning. If we can, though, just store the whole
+ * thing in the packet's option buffer and leave it at that.
+ */
+ if (option_size <= main_buffer_size - mainbufix) {
+ memcpy(&outpacket->options[mainbufix],
+ buffer, option_size);
+ mainbufix += option_size;
+ if (mainbufix < main_buffer_size)
+ outpacket->options[mainbufix++] = DHO_END;
+ length = DHCP_FIXED_NON_UDP + mainbufix;
+ } else {
+ outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
+ outpacket->options[mainbufix++] = 1;
+ if (option_size >
+ main_buffer_size - mainbufix + DHCP_FILE_LEN)
+ outpacket->options[mainbufix++] = 3;
+ else
+ outpacket->options[mainbufix++] = 1;
+
+ memcpy(&outpacket->options[mainbufix],
+ buffer, main_buffer_size - mainbufix);
+ bufix = main_buffer_size - mainbufix;
+ length = DHCP_FIXED_NON_UDP + mainbufix;
+ if (overload & 1) {
+ if (option_size - bufix <= DHCP_FILE_LEN) {
+ memcpy(outpacket->file,
+ &buffer[bufix], option_size - bufix);
+ mainbufix = option_size - bufix;
+ if (mainbufix < DHCP_FILE_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_END;
+ while (mainbufix < DHCP_FILE_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_PAD;
+ } else {
+ memcpy(outpacket->file,
+ &buffer[bufix], DHCP_FILE_LEN);
+ bufix += DHCP_FILE_LEN;
+ }
+ }
+ if ((overload & 2) && option_size < bufix) {
+ memcpy(outpacket->sname,
+ &buffer[bufix], option_size - bufix);
+
+ mainbufix = option_size - bufix;
+ if (mainbufix < DHCP_SNAME_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_END;
+ while (mainbufix < DHCP_SNAME_LEN)
+ outpacket->file[mainbufix++] = (char)DHO_PAD;
+ }
+ }
+ return (length);
+}
+
+/*
+ * Store all the requested options into the requested buffer.
+ */
+int
+store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
+ unsigned char *priority_list, int priority_len, int first_cutoff,
+ int second_cutoff, int terminate)
+{
+ int bufix = 0, option_stored[256], i, ix, tto;
+
+ /* Zero out the stored-lengths array. */
+ memset(option_stored, 0, sizeof(option_stored));
+
+ /*
+ * Copy out the options in the order that they appear in the
+ * priority list...
+ */
+ for (i = 0; i < priority_len; i++) {
+ /* Code for next option to try to store. */
+ int code = priority_list[i];
+ int optstart;
+
+ /*
+ * Number of bytes left to store (some may already have
+ * been stored by a previous pass).
+ */
+ int length;
+
+ /* If no data is available for this option, skip it. */
+ if (!options[code]) {
+ continue;
+ }
+
+ /*
+ * The client could ask for things that are mandatory,
+ * in which case we should avoid storing them twice...
+ */
+ if (option_stored[code])
+ continue;
+ option_stored[code] = 1;
+
+ /* We should now have a constant length for the option. */
+ length = options[code]->len;
+
+ /* Do we add a NUL? */
+ if (terminate && dhcp_options[code].format[0] == 't') {
+ length++;
+ tto = 1;
+ } else
+ tto = 0;
+
+ /* Try to store the option. */
+
+ /*
+ * If the option's length is more than 255, we must
+ * store it in multiple hunks. Store 255-byte hunks
+ * first. However, in any case, if the option data will
+ * cross a buffer boundary, split it across that
+ * boundary.
+ */
+ ix = 0;
+
+ optstart = bufix;
+ while (length) {
+ unsigned char incr = length > 255 ? 255 : length;
+
+ /*
+ * If this hunk of the buffer will cross a
+ * boundary, only go up to the boundary in this
+ * pass.
+ */
+ if (bufix < first_cutoff &&
+ bufix + incr > first_cutoff)
+ incr = first_cutoff - bufix;
+ else if (bufix < second_cutoff &&
+ bufix + incr > second_cutoff)
+ incr = second_cutoff - bufix;
+
+ /*
+ * If this option is going to overflow the
+ * buffer, skip it.
+ */
+ if (bufix + 2 + incr > buflen) {
+ bufix = optstart;
+ break;
+ }
+
+ /* Everything looks good - copy it in! */
+ buffer[bufix] = code;
+ buffer[bufix + 1] = incr;
+ if (tto && incr == length) {
+ memcpy(buffer + bufix + 2,
+ options[code]->value + ix, incr - 1);
+ buffer[bufix + 2 + incr - 1] = 0;
+ } else
+ memcpy(buffer + bufix + 2,
+ options[code]->value + ix, incr);
+ length -= incr;
+ ix += incr;
+ bufix += 2 + incr;
+ }
+ }
+ return (bufix);
+}
+
+/*
+ * Format the specified option so that a human can easily read it.
+ */
+char *
+pretty_print_option(unsigned int code, unsigned char *data, int len,
+ int emit_commas, int emit_quotes)
+{
+ static char optbuf[32768]; /* XXX */
+ int hunksize = 0, numhunk = -1, numelem = 0;
+ char fmtbuf[32], *op = optbuf;
+ int i, j, k, opleft = sizeof(optbuf);
+ unsigned char *dp = data;
+ struct in_addr foo;
+ char comma;
+
+ /* Code should be between 0 and 255. */
+ if (code > 255)
+ error("pretty_print_option: bad code %d", code);
+
+ if (emit_commas)
+ comma = ',';
+ else
+ comma = ' ';
+
+ /* Figure out the size of the data. */
+ for (i = 0; dhcp_options[code].format[i]; i++) {
+ if (!numhunk) {
+ warning("%s: Excess information in format string: %s",
+ dhcp_options[code].name,
+ &(dhcp_options[code].format[i]));
+ break;
+ }
+ numelem++;
+ fmtbuf[i] = dhcp_options[code].format[i];
+ switch (dhcp_options[code].format[i]) {
+ case 'A':
+ --numelem;
+ fmtbuf[i] = 0;
+ numhunk = 0;
+ break;
+ case 'X':
+ for (k = 0; k < len; k++)
+ if (!isascii(data[k]) ||
+ !isprint(data[k]))
+ break;
+ if (k == len) {
+ fmtbuf[i] = 't';
+ numhunk = -2;
+ } else {
+ fmtbuf[i] = 'x';
+ hunksize++;
+ comma = ':';
+ numhunk = 0;
+ }
+ fmtbuf[i + 1] = 0;
+ break;
+ case 't':
+ fmtbuf[i] = 't';
+ fmtbuf[i + 1] = 0;
+ numhunk = -2;
+ break;
+ case 'I':
+ case 'l':
+ case 'L':
+ hunksize += 4;
+ break;
+ case 's':
+ case 'S':
+ hunksize += 2;
+ break;
+ case 'b':
+ case 'B':
+ case 'f':
+ hunksize++;
+ break;
+ case 'e':
+ break;
+ default:
+ warning("%s: garbage in format string: %s",
+ dhcp_options[code].name,
+ &(dhcp_options[code].format[i]));
+ break;
+ }
+ }
+
+ /* Check for too few bytes... */
+ if (hunksize > len) {
+ warning("%s: expecting at least %d bytes; got %d",
+ dhcp_options[code].name, hunksize, len);
+ return ("<error>");
+ }
+ /* Check for too many bytes... */
+ if (numhunk == -1 && hunksize < len)
+ warning("%s: %d extra bytes",
+ dhcp_options[code].name, len - hunksize);
+
+ /* If this is an array, compute its size. */
+ if (!numhunk)
+ numhunk = len / hunksize;
+ /* See if we got an exact number of hunks. */
+ if (numhunk > 0 && numhunk * hunksize < len)
+ warning("%s: %d extra bytes at end of array",
+ dhcp_options[code].name, len - numhunk * hunksize);
+
+ /* A one-hunk array prints the same as a single hunk. */
+ if (numhunk < 0)
+ numhunk = 1;
+
+ /* Cycle through the array (or hunk) printing the data. */
+ for (i = 0; i < numhunk; i++) {
+ for (j = 0; j < numelem; j++) {
+ int opcount;
+ switch (fmtbuf[j]) {
+ case 't':
+ if (emit_quotes) {
+ *op++ = '"';
+ opleft--;
+ }
+ for (; dp < data + len; dp++) {
+ if (!isascii(*dp) ||
+ !isprint(*dp)) {
+ if (dp + 1 != data + len ||
+ *dp != 0) {
+ snprintf(op, opleft,
+ "\\%03o", *dp);
+ op += 4;
+ opleft -= 4;
+ }
+ } else if (*dp == '"' ||
+ *dp == '\'' ||
+ *dp == '$' ||
+ *dp == '`' ||
+ *dp == '\\') {
+ *op++ = '\\';
+ *op++ = *dp;
+ opleft -= 2;
+ } else {
+ *op++ = *dp;
+ opleft--;
+ }
+ }
+ if (emit_quotes) {
+ *op++ = '"';
+ opleft--;
+ }
+
+ *op = 0;
+ break;
+ case 'I':
+ foo.s_addr = htonl(getULong(dp));
+ opcount = strlcpy(op, inet_ntoa(foo), opleft);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 'l':
+ opcount = snprintf(op, opleft, "%ld",
+ (long)getLong(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 'L':
+ opcount = snprintf(op, opleft, "%ld",
+ (unsigned long)getULong(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 4;
+ break;
+ case 's':
+ opcount = snprintf(op, opleft, "%d",
+ getShort(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 2;
+ break;
+ case 'S':
+ opcount = snprintf(op, opleft, "%d",
+ getUShort(dp));
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ dp += 2;
+ break;
+ case 'b':
+ opcount = snprintf(op, opleft, "%d",
+ *(char *)dp++);
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'B':
+ opcount = snprintf(op, opleft, "%d", *dp++);
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'x':
+ opcount = snprintf(op, opleft, "%x", *dp++);
+ if (opcount >= opleft || opcount == -1)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ case 'f':
+ opcount = strlcpy(op,
+ *dp++ ? "true" : "false", opleft);
+ if (opcount >= opleft)
+ goto toobig;
+ opleft -= opcount;
+ break;
+ default:
+ warning("Unexpected format code %c", fmtbuf[j]);
+ }
+ op += strlen(op);
+ opleft -= strlen(op);
+ if (opleft < 1)
+ goto toobig;
+ if (j + 1 < numelem && comma != ':') {
+ *op++ = ' ';
+ opleft--;
+ }
+ }
+ if (i + 1 < numhunk) {
+ *op++ = comma;
+ opleft--;
+ }
+ if (opleft < 1)
+ goto toobig;
+
+ }
+ return (optbuf);
+ toobig:
+ warning("dhcp option too large");
+ return ("<error>");
+}
+
+void
+do_packet(struct interface_info *interface, struct dhcp_packet *packet,
+ int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
+{
+ struct packet tp;
+ int i;
+
+ if (packet->hlen > sizeof(packet->chaddr)) {
+ note("Discarding packet with invalid hlen.");
+ return;
+ }
+
+ memset(&tp, 0, sizeof(tp));
+ tp.raw = packet;
+ tp.packet_length = len;
+ tp.client_port = from_port;
+ tp.client_addr = from;
+ tp.interface = interface;
+ tp.haddr = hfrom;
+
+ parse_options(&tp);
+ if (tp.options_valid &&
+ tp.options[DHO_DHCP_MESSAGE_TYPE].data)
+ tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
+ if (tp.packet_type)
+ dhcp(&tp);
+ else
+ bootp(&tp);
+
+ /* Free the data associated with the options. */
+ for (i = 0; i < 256; i++)
+ if (tp.options[i].len && tp.options[i].data)
+ free(tp.options[i].data);
+}
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
new file mode 100644
index 000000000000..1a22044d40a1
--- /dev/null
+++ b/sbin/dhclient/packet.c
@@ -0,0 +1,253 @@
+/* $OpenBSD: packet.c,v 1.9 2004/05/04 18:58:50 deraadt Exp $ */
+
+/* Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/if_ether.h>
+
+#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t))
+
+u_int32_t checksum(unsigned char *, unsigned, u_int32_t);
+u_int32_t wrapsum(u_int32_t);
+
+void assemble_ethernet_header(struct interface_info *, unsigned char *,
+ int *, struct hardware *);
+ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
+ int bufix, struct hardware *);
+
+u_int32_t
+checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
+{
+ int i;
+
+ /* Checksum all the pairs of bytes first... */
+ for (i = 0; i < (nbytes & ~1U); i += 2) {
+ sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ /*
+ * If there's a single byte left over, checksum it, too.
+ * Network byte order is big-endian, so the remaining byte is
+ * the high byte.
+ */
+ if (i < nbytes) {
+ sum += buf[i] << 8;
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ return (sum);
+}
+
+u_int32_t
+wrapsum(u_int32_t sum)
+{
+ sum = ~sum & 0xFFFF;
+ return (htons(sum));
+}
+
+void
+assemble_hw_header(struct interface_info *interface, unsigned char *buf,
+ int *bufix, struct hardware *to)
+{
+ struct ether_header eh;
+
+ if (to != NULL && to->hlen == 6) /* XXX */
+ memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
+ else
+ memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+ if (interface->hw_address.hlen == sizeof(eh.ether_shost))
+ memcpy(eh.ether_shost, interface->hw_address.haddr,
+ sizeof(eh.ether_shost));
+ else
+ memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
+
+ eh.ether_type = htons(ETHERTYPE_IP);
+
+ memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE);
+ *bufix += ETHER_HEADER_SIZE;
+}
+
+void
+assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
+ u_int32_t to, unsigned int port, unsigned char *data, int len)
+{
+ struct ip ip;
+ struct udphdr udp;
+
+ ip.ip_v = 4;
+ ip.ip_hl = 5;
+ ip.ip_tos = IPTOS_LOWDELAY;
+ ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
+ ip.ip_id = 0;
+ ip.ip_off = 0;
+ ip.ip_ttl = 16;
+ ip.ip_p = IPPROTO_UDP;
+ ip.ip_sum = 0;
+ ip.ip_src.s_addr = from;
+ ip.ip_dst.s_addr = to;
+
+ ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
+ memcpy(&buf[*bufix], &ip, sizeof(ip));
+ *bufix += sizeof(ip);
+
+ udp.uh_sport = htons(LOCAL_PORT); /* XXX */
+ udp.uh_dport = port; /* XXX */
+ udp.uh_ulen = htons(sizeof(udp) + len);
+ memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
+
+ udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
+ checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ 2 * sizeof(ip.ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+
+ memcpy(&buf[*bufix], &udp, sizeof(udp));
+ *bufix += sizeof(udp);
+}
+
+ssize_t
+decode_hw_header(unsigned char *buf, int bufix, struct hardware *from)
+{
+ struct ether_header eh;
+
+ memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE);
+
+ memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
+ from->htype = ARPHRD_ETHER;
+ from->hlen = sizeof(eh.ether_shost);
+
+ return (sizeof(eh));
+}
+
+ssize_t
+decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
+ unsigned char *data, int buflen)
+{
+ struct ip *ip;
+ struct udphdr *udp;
+ u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
+ u_int32_t sum, usum;
+ static int ip_packets_seen;
+ static int ip_packets_bad_checksum;
+ static int udp_packets_seen;
+ static int udp_packets_bad_checksum;
+ static int udp_packets_length_checked;
+ static int udp_packets_length_overflow;
+ int len = 0;
+
+ ip = (struct ip *)(buf + bufix);
+ udp = (struct udphdr *)(buf + bufix + ip_len);
+
+ /* Check the IP header checksum - it should be zero. */
+ ip_packets_seen++;
+ if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
+ ip_packets_bad_checksum++;
+ if (ip_packets_seen > 4 &&
+ (ip_packets_seen / ip_packets_bad_checksum) < 2) {
+ note("%d bad IP checksums seen in %d packets",
+ ip_packets_bad_checksum, ip_packets_seen);
+ ip_packets_seen = ip_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ if (ntohs(ip->ip_len) != buflen)
+ debug("ip length %d disagrees with bytes received %d.",
+ ntohs(ip->ip_len), buflen);
+
+ memcpy(&from->sin_addr, &ip->ip_src, 4);
+
+ /*
+ * Compute UDP checksums, including the ``pseudo-header'', the
+ * UDP header and the data. If the UDP checksum field is zero,
+ * we're not supposed to do a checksum.
+ */
+ if (!data) {
+ data = buf + bufix + ip_len + sizeof(*udp);
+ len = ntohs(udp->uh_ulen) - sizeof(*udp);
+ udp_packets_length_checked++;
+ if (len + data > buf + bufix + buflen) {
+ udp_packets_length_overflow++;
+ if (udp_packets_length_checked > 4 &&
+ (udp_packets_length_checked /
+ udp_packets_length_overflow) < 2) {
+ note("%d udp packets in %d too long - dropped",
+ udp_packets_length_overflow,
+ udp_packets_length_checked);
+ udp_packets_length_overflow =
+ udp_packets_length_checked = 0;
+ }
+ return (-1);
+ }
+ if (len + data != buf + bufix + buflen)
+ debug("accepting packet with data after udp payload.");
+ }
+
+ usum = udp->uh_sum;
+ udp->uh_sum = 0;
+
+ sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
+ checksum(data, len, checksum((unsigned char *)&ip->ip_src,
+ 2 * sizeof(ip->ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
+
+ udp_packets_seen++;
+ if (usum && usum != sum) {
+ udp_packets_bad_checksum++;
+ if (udp_packets_seen > 4 &&
+ (udp_packets_seen / udp_packets_bad_checksum) < 2) {
+ note("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return (-1);
+ }
+
+ memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
+
+ return (ip_len + sizeof(*udp));
+}
diff --git a/sbin/dhclient/parse.c b/sbin/dhclient/parse.c
new file mode 100644
index 000000000000..e8870bc12ae3
--- /dev/null
+++ b/sbin/dhclient/parse.c
@@ -0,0 +1,577 @@
+/* $OpenBSD: parse.c,v 1.11 2004/05/05 23:07:47 deraadt Exp $ */
+
+/* Common parser code for dhcpd and dhclient. */
+
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+/* Skip to the semicolon ending the current statement. If we encounter
+ * braces, the matching closing brace terminates the statement. If we
+ * encounter a right brace but haven't encountered a left brace, return
+ * leaving the brace in the token buffer for the caller. If we see a
+ * semicolon and haven't seen a left brace, return. This lets us skip
+ * over:
+ *
+ * statement;
+ * statement foo bar { }
+ * statement foo bar { statement { } }
+ * statement}
+ *
+ * ...et cetera.
+ */
+void
+skip_to_semi(FILE *cfile)
+{
+ int brace_count = 0, token;
+ char *val;
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == RBRACE) {
+ if (brace_count) {
+ token = next_token(&val, cfile);
+ if (!--brace_count)
+ return;
+ } else
+ return;
+ } else if (token == LBRACE) {
+ brace_count++;
+ } else if (token == SEMI && !brace_count) {
+ token = next_token(&val, cfile);
+ return;
+ } else if (token == '\n') {
+ /*
+ * EOL only happens when parsing
+ * /etc/resolv.conf, and we treat it like a
+ * semicolon because the resolv.conf file is
+ * line-oriented.
+ */
+ token = next_token(&val, cfile);
+ return;
+ }
+ token = next_token(&val, cfile);
+ } while (token != EOF);
+}
+
+int
+parse_semi(FILE *cfile)
+{
+ int token;
+ char *val;
+
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * string-parameter :== STRING SEMI
+ */
+char *
+parse_string(FILE *cfile)
+{
+ char *val, *s;
+ int token;
+
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("filename must be a string");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ s = malloc(strlen(val) + 1);
+ if (!s)
+ error("no memory for string %s.", val);
+ strlcpy(s, val, strlen(val) + 1);
+
+ if (!parse_semi(cfile))
+ return (NULL);
+ return (s);
+}
+
+int
+parse_ip_addr(FILE *cfile, struct iaddr *addr)
+{
+ addr->len = 4;
+ if (parse_numeric_aggregate(cfile, addr->iabuf,
+ &addr->len, DOT, 10, 8))
+ return (1);
+ return (0);
+}
+
+/*
+ * hardware-parameter :== HARDWARE ETHERNET csns SEMI
+ * csns :== NUMBER | csns COLON NUMBER
+ */
+void
+parse_hardware_param(FILE *cfile, struct hardware *hardware)
+{
+ unsigned char *t;
+ int token, hlen;
+ char *val;
+
+ token = next_token(&val, cfile);
+ switch (token) {
+ case ETHERNET:
+ hardware->htype = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ hardware->htype = HTYPE_IEEE802;
+ break;
+ case FDDI:
+ hardware->htype = HTYPE_FDDI;
+ break;
+ default:
+ parse_warn("expecting a network hardware type");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Parse the hardware address information. Technically, it
+ * would make a lot of sense to restrict the length of the data
+ * we'll accept here to the length of a particular hardware
+ * address type. Unfortunately, there are some broken clients
+ * out there that put bogus data in the chaddr buffer, and we
+ * accept that data in the lease file rather than simply failing
+ * on such clients. Yuck.
+ */
+ hlen = 0;
+ t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8);
+ if (!t)
+ return;
+ if (hlen > sizeof(hardware->haddr)) {
+ free(t);
+ parse_warn("hardware address too long");
+ } else {
+ hardware->hlen = hlen;
+ memcpy((unsigned char *)&hardware->haddr[0], t,
+ hardware->hlen);
+ if (hlen < sizeof(hardware->haddr))
+ memset(&hardware->haddr[hlen], 0,
+ sizeof(hardware->haddr) - hlen);
+ free(t);
+ }
+
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+/*
+ * lease-time :== NUMBER SEMI
+ */
+void
+parse_lease_time(FILE *cfile, time_t *timep)
+{
+ char *val;
+ int token;
+
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("Expecting numeric lease time");
+ skip_to_semi(cfile);
+ return;
+ }
+ convert_num((unsigned char *)timep, val, 10, 32);
+ /* Unswap the number - convert_num returns stuff in NBO. */
+ *timep = ntohl(*timep); /* XXX */
+
+ parse_semi(cfile);
+}
+
+/*
+ * No BNF for numeric aggregates - that's defined by the caller. What
+ * this function does is to parse a sequence of numbers separated by the
+ * token specified in separator. If max is zero, any number of numbers
+ * will be parsed; otherwise, exactly max numbers are expected. Base
+ * and size tell us how to internalize the numbers once they've been
+ * tokenized.
+ */
+unsigned char *
+parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
+ int separator, int base, int size)
+{
+ unsigned char *bufp = buf, *s = NULL;
+ int token, count = 0;
+ char *val, *t;
+ pair c = NULL;
+
+ if (!bufp && *max) {
+ bufp = malloc(*max * size / 8);
+ if (!bufp)
+ error("can't allocate space for numeric aggregate");
+ } else
+ s = bufp;
+
+ do {
+ if (count) {
+ token = peek_token(&val, cfile);
+ if (token != separator) {
+ if (!*max)
+ break;
+ if (token != RBRACE && token != LBRACE)
+ token = next_token(&val, cfile);
+ parse_warn("too few numbers.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ token = next_token(&val, cfile);
+ }
+ token = next_token(&val, cfile);
+
+ if (token == EOF) {
+ parse_warn("unexpected end of file");
+ break;
+ }
+
+ /* Allow NUMBER_OR_NAME if base is 16. */
+ if (token != NUMBER &&
+ (base != 16 || token != NUMBER_OR_NAME)) {
+ parse_warn("expecting numeric value.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ /*
+ * If we can, convert the number now; otherwise, build a
+ * linked list of all the numbers.
+ */
+ if (s) {
+ convert_num(s, val, base, size);
+ s += size / 8;
+ } else {
+ t = malloc(strlen(val) + 1);
+ if (!t)
+ error("no temp space for number.");
+ strlcpy(t, val, strlen(val) + 1);
+ c = cons(t, c);
+ }
+ } while (++count != *max);
+
+ /* If we had to cons up a list, convert it now. */
+ if (c) {
+ bufp = malloc(count * size / 8);
+ if (!bufp)
+ error("can't allocate space for numeric aggregate.");
+ s = bufp + count - size / 8;
+ *max = count;
+ }
+ while (c) {
+ pair cdr = c->cdr;
+ convert_num(s, (char *)c->car, base, size);
+ s -= size / 8;
+ /* Free up temp space. */
+ free(c->car);
+ free(c);
+ c = cdr;
+ }
+ return (bufp);
+}
+
+void
+convert_num(unsigned char *buf, char *str, int base, int size)
+{
+ int negative = 0, tval, max;
+ u_int32_t val = 0;
+ char *ptr = str;
+
+ if (*ptr == '-') {
+ negative = 1;
+ ptr++;
+ }
+
+ /* If base wasn't specified, figure it out from the data. */
+ if (!base) {
+ if (ptr[0] == '0') {
+ if (ptr[1] == 'x') {
+ base = 16;
+ ptr += 2;
+ } else if (isascii(ptr[1]) && isdigit(ptr[1])) {
+ base = 8;
+ ptr += 1;
+ } else
+ base = 10;
+ } else
+ base = 10;
+ }
+
+ do {
+ tval = *ptr++;
+ /* XXX assumes ASCII... */
+ if (tval >= 'a')
+ tval = tval - 'a' + 10;
+ else if (tval >= 'A')
+ tval = tval - 'A' + 10;
+ else if (tval >= '0')
+ tval -= '0';
+ else {
+ warning("Bogus number: %s.", str);
+ break;
+ }
+ if (tval >= base) {
+ warning("Bogus number: %s: digit %d not in base %d",
+ str, tval, base);
+ break;
+ }
+ val = val * base + tval;
+ } while (*ptr);
+
+ if (negative)
+ max = (1 << (size - 1));
+ else
+ max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
+ if (val > max) {
+ switch (base) {
+ case 8:
+ warning("value %s%o exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ case 16:
+ warning("value %s%x exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ default:
+ warning("value %s%u exceeds max (%d) for precision.",
+ negative ? "-" : "", val, max);
+ break;
+ }
+ }
+
+ if (negative)
+ switch (size) {
+ case 8:
+ *buf = -(unsigned long)val;
+ break;
+ case 16:
+ putShort(buf, -(unsigned long)val);
+ break;
+ case 32:
+ putLong(buf, -(unsigned long)val);
+ break;
+ default:
+ warning("Unexpected integer size: %d", size);
+ break;
+ }
+ else
+ switch (size) {
+ case 8:
+ *buf = (u_int8_t)val;
+ break;
+ case 16:
+ putUShort(buf, (u_int16_t)val);
+ break;
+ case 32:
+ putULong(buf, val);
+ break;
+ default:
+ warning("Unexpected integer size: %d", size);
+ break;
+ }
+}
+
+/*
+ * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER SEMI
+ *
+ * Dates are always in GMT; first number is day of week; next is
+ * year/month/day; next is hours:minutes:seconds on a 24-hour
+ * clock.
+ */
+time_t
+parse_date(FILE *cfile)
+{
+ static int months[11] = { 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334 };
+ int guess, token;
+ struct tm tm;
+ char *val;
+
+ /* Day of week... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric day of week expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_wday = atoi(val);
+
+ /* Year... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric year expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_year = atoi(val);
+ if (tm.tm_year > 1900)
+ tm.tm_year -= 1900;
+
+ /* Slash separating year from month... */
+ token = next_token(&val, cfile);
+ if (token != SLASH) {
+ parse_warn("expected slash separating year from month.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Month... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric month expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_mon = atoi(val) - 1;
+
+ /* Slash separating month from day... */
+ token = next_token(&val, cfile);
+ if (token != SLASH) {
+ parse_warn("expected slash separating month from day.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Month... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric day of month expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_mday = atoi(val);
+
+ /* Hour... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric hour expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_hour = atoi(val);
+
+ /* Colon separating hour from minute... */
+ token = next_token(&val, cfile);
+ if (token != COLON) {
+ parse_warn("expected colon separating hour from minute.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Minute... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric minute expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_min = atoi(val);
+
+ /* Colon separating minute from second... */
+ token = next_token(&val, cfile);
+ if (token != COLON) {
+ parse_warn("expected colon separating hour from minute.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Minute... */
+ token = next_token(&val, cfile);
+ if (token != NUMBER) {
+ parse_warn("numeric minute expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ tm.tm_sec = atoi(val);
+ tm.tm_isdst = 0;
+
+ /* XXX: We assume that mktime does not use tm_yday. */
+ tm.tm_yday = 0;
+
+ /* Make sure the date ends in a semicolon... */
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ /* Guess the time value... */
+ guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */
+ (tm.tm_year - 69) / 4 + /* Leap days since '70 */
+ (tm.tm_mon /* Days in months this year */
+ ? months[tm.tm_mon - 1]
+ : 0) +
+ (tm.tm_mon > 1 && /* Leap day this year */
+ !((tm.tm_year - 72) & 3)) +
+ tm.tm_mday - 1) * 24) + /* Day of month */
+ tm.tm_hour) * 60) +
+ tm.tm_min) * 60) + tm.tm_sec;
+
+ /*
+ * This guess could be wrong because of leap seconds or other
+ * weirdness we don't know about that the system does. For
+ * now, we're just going to accept the guess, but at some point
+ * it might be nice to do a successive approximation here to get
+ * an exact value. Even if the error is small, if the server
+ * is restarted frequently (and thus the lease database is
+ * reread), the error could accumulate into something
+ * significant.
+ */
+ return (guess);
+}
diff --git a/sbin/dhclient/privsep.c b/sbin/dhclient/privsep.c
new file mode 100644
index 000000000000..cf47e56ceb6e
--- /dev/null
+++ b/sbin/dhclient/privsep.c
@@ -0,0 +1,235 @@
+/* $OpenBSD: privsep.c,v 1.7 2004/05/10 18:34:42 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "dhcpd.h"
+#include "privsep.h"
+
+struct buf *
+buf_open(size_t len)
+{
+ struct buf *buf;
+
+ if ((buf = calloc(1, sizeof(struct buf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = len;
+
+ return (buf);
+}
+
+int
+buf_add(struct buf *buf, void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+int
+buf_close(int sock, struct buf *buf)
+{
+ ssize_t n;
+
+ do {
+ n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos);
+ if (n != -1)
+ buf->rpos += n;
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-1);
+ }
+ } while (n == -1 && (errno == EAGAIN || errno == EINTR));
+
+ if (buf->rpos < buf->size)
+ error("short write: wanted %lu got %ld bytes",
+ (unsigned long)buf->size, (long)buf->rpos);
+
+ free(buf->buf);
+ free(buf);
+ return (n);
+}
+
+ssize_t
+buf_read(int sock, void *buf, size_t nbytes)
+{
+ ssize_t n, r = 0;
+ char *p = buf;
+
+ do {
+ n = read(sock, p, nbytes);
+ if (n == 0)
+ error("connection closed");
+ if (n != -1) {
+ r += n;
+ p += n;
+ nbytes -= n;
+ }
+ } while (n == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (n == -1)
+ error("buf_read: %m");
+
+ if (r < nbytes)
+ error("short read: wanted %lu got %ld bytes",
+ (unsigned long)nbytes, (long)r);
+
+ return (r);
+}
+
+void
+dispatch_imsg(int fd)
+{
+ struct imsg_hdr hdr;
+ char *medium, *reason, *filename,
+ *servername, *prefix;
+ size_t medium_len, reason_len, filename_len,
+ servername_len, prefix_len, totlen;
+ struct client_lease lease;
+ int ret, i, optlen;
+ struct buf *buf;
+
+ buf_read(fd, &hdr, sizeof(hdr));
+
+ switch (hdr.code) {
+ case IMSG_SCRIPT_INIT:
+ if (hdr.len < sizeof(hdr) + sizeof(size_t))
+ error("corrupted message received");
+ buf_read(fd, &medium_len, sizeof(medium_len));
+ if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr)
+ + sizeof(size_t) || medium_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (medium_len > 0) {
+ if ((medium = calloc(1, medium_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, medium, medium_len);
+ } else
+ medium = NULL;
+
+ buf_read(fd, &reason_len, sizeof(reason_len));
+ if (hdr.len < medium_len + reason_len + sizeof(hdr) ||
+ reason_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (reason_len > 0) {
+ if ((reason = calloc(1, reason_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, reason, reason_len);
+ } else
+ reason = NULL;
+
+ priv_script_init(reason, medium);
+ free(reason);
+ free(medium);
+ break;
+ case IMSG_SCRIPT_WRITE_PARAMS:
+ bzero(&lease, sizeof lease);
+ totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ buf_read(fd, &lease, sizeof(lease));
+
+ buf_read(fd, &filename_len, sizeof(filename_len));
+ totlen += filename_len + sizeof(size_t);
+ if (hdr.len < totlen || filename_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (filename_len > 0) {
+ if ((filename = calloc(1, filename_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, filename, filename_len);
+ } else
+ filename = NULL;
+
+ buf_read(fd, &servername_len, sizeof(servername_len));
+ totlen += servername_len + sizeof(size_t);
+ if (hdr.len < totlen || servername_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (servername_len > 0) {
+ if ((servername =
+ calloc(1, servername_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, servername, servername_len);
+ } else
+ servername = NULL;
+
+ buf_read(fd, &prefix_len, sizeof(prefix_len));
+ totlen += prefix_len;
+ if (hdr.len < totlen || prefix_len == SIZE_T_MAX)
+ error("corrupted message received");
+ if (prefix_len > 0) {
+ if ((prefix = calloc(1, prefix_len + 1)) == NULL)
+ error("%m");
+ buf_read(fd, prefix, prefix_len);
+ } else
+ prefix = NULL;
+
+ for (i = 0; i < 256; i++) {
+ totlen += sizeof(optlen);
+ if (hdr.len < totlen)
+ error("corrupted message received");
+ buf_read(fd, &optlen, sizeof(optlen));
+ lease.options[i].data = NULL;
+ lease.options[i].len = optlen;
+ if (optlen > 0) {
+ totlen += optlen;
+ if (hdr.len < totlen || optlen == SIZE_T_MAX)
+ error("corrupted message received");
+ lease.options[i].data =
+ calloc(1, optlen + 1);
+ if (lease.options[i].data == NULL)
+ error("%m");
+ buf_read(fd, lease.options[i].data, optlen);
+ }
+ }
+ lease.server_name = servername;
+ lease.filename = filename;
+
+ priv_script_write_params(prefix, &lease);
+
+ free(servername);
+ free(filename);
+ free(prefix);
+ for (i = 0; i < 256; i++)
+ if (lease.options[i].len > 0)
+ free(lease.options[i].data);
+ break;
+ case IMSG_SCRIPT_GO:
+ if (hdr.len != sizeof(hdr))
+ error("corrupted message received");
+
+ ret = priv_script_go();
+
+ hdr.code = IMSG_SCRIPT_GO_RET;
+ hdr.len = sizeof(struct imsg_hdr) + sizeof(int);
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+ if (buf_add(buf, &hdr, sizeof(hdr)))
+ error("buf_add: %m");
+ if (buf_add(buf, &ret, sizeof(ret)))
+ error("buf_add: %m");
+ if (buf_close(fd, buf) == -1)
+ error("buf_close: %m");
+ break;
+ default:
+ error("received unknown message, code %d", hdr.code);
+ }
+}
diff --git a/sbin/dhclient/privsep.h b/sbin/dhclient/privsep.h
new file mode 100644
index 000000000000..f30284eeefeb
--- /dev/null
+++ b/sbin/dhclient/privsep.h
@@ -0,0 +1,47 @@
+/* $OpenBSD: privsep.h,v 1.2 2004/05/04 18:51:18 henning Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <poll.h>
+#include <pwd.h>
+
+struct buf {
+ u_char *buf;
+ size_t size;
+ size_t wpos;
+ size_t rpos;
+};
+
+enum imsg_code {
+ IMSG_NONE,
+ IMSG_SCRIPT_INIT,
+ IMSG_SCRIPT_WRITE_PARAMS,
+ IMSG_SCRIPT_GO,
+ IMSG_SCRIPT_GO_RET
+};
+
+struct imsg_hdr {
+ enum imsg_code code;
+ size_t len;
+};
+
+struct buf *buf_open(size_t);
+int buf_add(struct buf *, void *, size_t);
+int buf_close(int, struct buf *);
+ssize_t buf_read(int sock, void *, size_t);
diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c
new file mode 100644
index 000000000000..6648756952c9
--- /dev/null
+++ b/sbin/dhclient/tables.c
@@ -0,0 +1,430 @@
+/* $OpenBSD: tables.c,v 1.4 2004/05/04 20:28:40 deraadt Exp $ */
+
+/* Tables of information... */
+
+/*
+ * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+/*
+ * DHCP Option names, formats and codes, from RFC1533.
+ *
+ * Format codes:
+ *
+ * e - end of data
+ * I - IP address
+ * l - 32-bit signed integer
+ * L - 32-bit unsigned integer
+ * s - 16-bit signed integer
+ * S - 16-bit unsigned integer
+ * b - 8-bit signed integer
+ * B - 8-bit unsigned integer
+ * t - ASCII text
+ * f - flag (true or false)
+ * A - array of whatever precedes (e.g., IA means array of IP addresses)
+ */
+
+struct universe dhcp_universe;
+struct option dhcp_options[256] = {
+ { "pad", "", &dhcp_universe, 0 },
+ { "subnet-mask", "I", &dhcp_universe, 1 },
+ { "time-offset", "l", &dhcp_universe, 2 },
+ { "routers", "IA", &dhcp_universe, 3 },
+ { "time-servers", "IA", &dhcp_universe, 4 },
+ { "ien116-name-servers", "IA", &dhcp_universe, 5 },
+ { "domain-name-servers", "IA", &dhcp_universe, 6 },
+ { "log-servers", "IA", &dhcp_universe, 7 },
+ { "cookie-servers", "IA", &dhcp_universe, 8 },
+ { "lpr-servers", "IA", &dhcp_universe, 9 },
+ { "impress-servers", "IA", &dhcp_universe, 10 },
+ { "resource-location-servers", "IA", &dhcp_universe, 11 },
+ { "host-name", "X", &dhcp_universe, 12 },
+ { "boot-size", "S", &dhcp_universe, 13 },
+ { "merit-dump", "t", &dhcp_universe, 14 },
+ { "domain-name", "t", &dhcp_universe, 15 },
+ { "swap-server", "I", &dhcp_universe, 16 },
+ { "root-path", "t", &dhcp_universe, 17 },
+ { "extensions-path", "t", &dhcp_universe, 18 },
+ { "ip-forwarding", "f", &dhcp_universe, 19 },
+ { "non-local-source-routing", "f", &dhcp_universe, 20 },
+ { "policy-filter", "IIA", &dhcp_universe, 21 },
+ { "max-dgram-reassembly", "S", &dhcp_universe, 22 },
+ { "default-ip-ttl", "B", &dhcp_universe, 23 },
+ { "path-mtu-aging-timeout", "L", &dhcp_universe, 24 },
+ { "path-mtu-plateau-table", "SA", &dhcp_universe, 25 },
+ { "interface-mtu", "S", &dhcp_universe, 26 },
+ { "all-subnets-local", "f", &dhcp_universe, 27 },
+ { "broadcast-address", "I", &dhcp_universe, 28 },
+ { "perform-mask-discovery", "f", &dhcp_universe, 29 },
+ { "mask-supplier", "f", &dhcp_universe, 30 },
+ { "router-discovery", "f", &dhcp_universe, 31 },
+ { "router-solicitation-address", "I", &dhcp_universe, 32 },
+ { "static-routes", "IIA", &dhcp_universe, 33 },
+ { "trailer-encapsulation", "f", &dhcp_universe, 34 },
+ { "arp-cache-timeout", "L", &dhcp_universe, 35 },
+ { "ieee802-3-encapsulation", "f", &dhcp_universe, 36 },
+ { "default-tcp-ttl", "B", &dhcp_universe, 37 },
+ { "tcp-keepalive-interval", "L", &dhcp_universe, 38 },
+ { "tcp-keepalive-garbage", "f", &dhcp_universe, 39 },
+ { "nis-domain", "t", &dhcp_universe, 40 },
+ { "nis-servers", "IA", &dhcp_universe, 41 },
+ { "ntp-servers", "IA", &dhcp_universe, 42 },
+ { "vendor-encapsulated-options", "X", &dhcp_universe, 43 },
+ { "netbios-name-servers", "IA", &dhcp_universe, 44 },
+ { "netbios-dd-server", "IA", &dhcp_universe, 45 },
+ { "netbios-node-type", "B", &dhcp_universe, 46 },
+ { "netbios-scope", "t", &dhcp_universe, 47 },
+ { "font-servers", "IA", &dhcp_universe, 48 },
+ { "x-display-manager", "IA", &dhcp_universe, 49 },
+ { "dhcp-requested-address", "I", &dhcp_universe, 50 },
+ { "dhcp-lease-time", "L", &dhcp_universe, 51 },
+ { "dhcp-option-overload", "B", &dhcp_universe, 52 },
+ { "dhcp-message-type", "B", &dhcp_universe, 53 },
+ { "dhcp-server-identifier", "I", &dhcp_universe, 54 },
+ { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55 },
+ { "dhcp-message", "t", &dhcp_universe, 56 },
+ { "dhcp-max-message-size", "S", &dhcp_universe, 57 },
+ { "dhcp-renewal-time", "L", &dhcp_universe, 58 },
+ { "dhcp-rebinding-time", "L", &dhcp_universe, 59 },
+ { "dhcp-class-identifier", "t", &dhcp_universe, 60 },
+ { "dhcp-client-identifier", "X", &dhcp_universe, 61 },
+ { "option-62", "X", &dhcp_universe, 62 },
+ { "option-63", "X", &dhcp_universe, 63 },
+ { "nisplus-domain", "t", &dhcp_universe, 64 },
+ { "nisplus-servers", "IA", &dhcp_universe, 65 },
+ { "tftp-server-name", "t", &dhcp_universe, 66 },
+ { "bootfile-name", "t", &dhcp_universe, 67 },
+ { "mobile-ip-home-agent", "IA", &dhcp_universe, 68 },
+ { "smtp-server", "IA", &dhcp_universe, 69 },
+ { "pop-server", "IA", &dhcp_universe, 70 },
+ { "nntp-server", "IA", &dhcp_universe, 71 },
+ { "www-server", "IA", &dhcp_universe, 72 },
+ { "finger-server", "IA", &dhcp_universe, 73 },
+ { "irc-server", "IA", &dhcp_universe, 74 },
+ { "streettalk-server", "IA", &dhcp_universe, 75 },
+ { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 },
+ { "user-class", "t", &dhcp_universe, 77 },
+ { "option-78", "X", &dhcp_universe, 78 },
+ { "option-79", "X", &dhcp_universe, 79 },
+ { "option-80", "X", &dhcp_universe, 80 },
+ { "option-81", "X", &dhcp_universe, 81 },
+ { "option-82", "X", &dhcp_universe, 82 },
+ { "option-83", "X", &dhcp_universe, 83 },
+ { "option-84", "X", &dhcp_universe, 84 },
+ { "nds-servers", "IA", &dhcp_universe, 85 },
+ { "nds-tree-name", "X", &dhcp_universe, 86 },
+ { "nds-context", "X", &dhcp_universe, 87 },
+ { "option-88", "X", &dhcp_universe, 88 },
+ { "option-89", "X", &dhcp_universe, 89 },
+ { "option-90", "X", &dhcp_universe, 90 },
+ { "option-91", "X", &dhcp_universe, 91 },
+ { "option-92", "X", &dhcp_universe, 92 },
+ { "option-93", "X", &dhcp_universe, 93 },
+ { "option-94", "X", &dhcp_universe, 94 },
+ { "option-95", "X", &dhcp_universe, 95 },
+ { "option-96", "X", &dhcp_universe, 96 },
+ { "option-97", "X", &dhcp_universe, 97 },
+ { "option-98", "X", &dhcp_universe, 98 },
+ { "option-99", "X", &dhcp_universe, 99 },
+ { "option-100", "X", &dhcp_universe, 100 },
+ { "option-101", "X", &dhcp_universe, 101 },
+ { "option-102", "X", &dhcp_universe, 102 },
+ { "option-103", "X", &dhcp_universe, 103 },
+ { "option-104", "X", &dhcp_universe, 104 },
+ { "option-105", "X", &dhcp_universe, 105 },
+ { "option-106", "X", &dhcp_universe, 106 },
+ { "option-107", "X", &dhcp_universe, 107 },
+ { "option-108", "X", &dhcp_universe, 108 },
+ { "option-109", "X", &dhcp_universe, 109 },
+ { "option-110", "X", &dhcp_universe, 110 },
+ { "option-111", "X", &dhcp_universe, 111 },
+ { "option-112", "X", &dhcp_universe, 112 },
+ { "option-113", "X", &dhcp_universe, 113 },
+ { "option-114", "X", &dhcp_universe, 114 },
+ { "option-115", "X", &dhcp_universe, 115 },
+ { "option-116", "X", &dhcp_universe, 116 },
+ { "option-117", "X", &dhcp_universe, 117 },
+ { "option-118", "X", &dhcp_universe, 118 },
+ { "option-119", "X", &dhcp_universe, 119 },
+ { "option-120", "X", &dhcp_universe, 120 },
+ { "option-121", "X", &dhcp_universe, 121 },
+ { "option-122", "X", &dhcp_universe, 122 },
+ { "option-123", "X", &dhcp_universe, 123 },
+ { "option-124", "X", &dhcp_universe, 124 },
+ { "option-125", "X", &dhcp_universe, 125 },
+ { "option-126", "X", &dhcp_universe, 126 },
+ { "option-127", "X", &dhcp_universe, 127 },
+ { "option-128", "X", &dhcp_universe, 128 },
+ { "option-129", "X", &dhcp_universe, 129 },
+ { "option-130", "X", &dhcp_universe, 130 },
+ { "option-131", "X", &dhcp_universe, 131 },
+ { "option-132", "X", &dhcp_universe, 132 },
+ { "option-133", "X", &dhcp_universe, 133 },
+ { "option-134", "X", &dhcp_universe, 134 },
+ { "option-135", "X", &dhcp_universe, 135 },
+ { "option-136", "X", &dhcp_universe, 136 },
+ { "option-137", "X", &dhcp_universe, 137 },
+ { "option-138", "X", &dhcp_universe, 138 },
+ { "option-139", "X", &dhcp_universe, 139 },
+ { "option-140", "X", &dhcp_universe, 140 },
+ { "option-141", "X", &dhcp_universe, 141 },
+ { "option-142", "X", &dhcp_universe, 142 },
+ { "option-143", "X", &dhcp_universe, 143 },
+ { "option-144", "X", &dhcp_universe, 144 },
+ { "option-145", "X", &dhcp_universe, 145 },
+ { "option-146", "X", &dhcp_universe, 146 },
+ { "option-147", "X", &dhcp_universe, 147 },
+ { "option-148", "X", &dhcp_universe, 148 },
+ { "option-149", "X", &dhcp_universe, 149 },
+ { "option-150", "X", &dhcp_universe, 150 },
+ { "option-151", "X", &dhcp_universe, 151 },
+ { "option-152", "X", &dhcp_universe, 152 },
+ { "option-153", "X", &dhcp_universe, 153 },
+ { "option-154", "X", &dhcp_universe, 154 },
+ { "option-155", "X", &dhcp_universe, 155 },
+ { "option-156", "X", &dhcp_universe, 156 },
+ { "option-157", "X", &dhcp_universe, 157 },
+ { "option-158", "X", &dhcp_universe, 158 },
+ { "option-159", "X", &dhcp_universe, 159 },
+ { "option-160", "X", &dhcp_universe, 160 },
+ { "option-161", "X", &dhcp_universe, 161 },
+ { "option-162", "X", &dhcp_universe, 162 },
+ { "option-163", "X", &dhcp_universe, 163 },
+ { "option-164", "X", &dhcp_universe, 164 },
+ { "option-165", "X", &dhcp_universe, 165 },
+ { "option-166", "X", &dhcp_universe, 166 },
+ { "option-167", "X", &dhcp_universe, 167 },
+ { "option-168", "X", &dhcp_universe, 168 },
+ { "option-169", "X", &dhcp_universe, 169 },
+ { "option-170", "X", &dhcp_universe, 170 },
+ { "option-171", "X", &dhcp_universe, 171 },
+ { "option-172", "X", &dhcp_universe, 172 },
+ { "option-173", "X", &dhcp_universe, 173 },
+ { "option-174", "X", &dhcp_universe, 174 },
+ { "option-175", "X", &dhcp_universe, 175 },
+ { "option-176", "X", &dhcp_universe, 176 },
+ { "option-177", "X", &dhcp_universe, 177 },
+ { "option-178", "X", &dhcp_universe, 178 },
+ { "option-179", "X", &dhcp_universe, 179 },
+ { "option-180", "X", &dhcp_universe, 180 },
+ { "option-181", "X", &dhcp_universe, 181 },
+ { "option-182", "X", &dhcp_universe, 182 },
+ { "option-183", "X", &dhcp_universe, 183 },
+ { "option-184", "X", &dhcp_universe, 184 },
+ { "option-185", "X", &dhcp_universe, 185 },
+ { "option-186", "X", &dhcp_universe, 186 },
+ { "option-187", "X", &dhcp_universe, 187 },
+ { "option-188", "X", &dhcp_universe, 188 },
+ { "option-189", "X", &dhcp_universe, 189 },
+ { "option-190", "X", &dhcp_universe, 190 },
+ { "option-191", "X", &dhcp_universe, 191 },
+ { "option-192", "X", &dhcp_universe, 192 },
+ { "option-193", "X", &dhcp_universe, 193 },
+ { "option-194", "X", &dhcp_universe, 194 },
+ { "option-195", "X", &dhcp_universe, 195 },
+ { "option-196", "X", &dhcp_universe, 196 },
+ { "option-197", "X", &dhcp_universe, 197 },
+ { "option-198", "X", &dhcp_universe, 198 },
+ { "option-199", "X", &dhcp_universe, 199 },
+ { "option-200", "X", &dhcp_universe, 200 },
+ { "option-201", "X", &dhcp_universe, 201 },
+ { "option-202", "X", &dhcp_universe, 202 },
+ { "option-203", "X", &dhcp_universe, 203 },
+ { "option-204", "X", &dhcp_universe, 204 },
+ { "option-205", "X", &dhcp_universe, 205 },
+ { "option-206", "X", &dhcp_universe, 206 },
+ { "option-207", "X", &dhcp_universe, 207 },
+ { "option-208", "X", &dhcp_universe, 208 },
+ { "option-209", "X", &dhcp_universe, 209 },
+ { "option-210", "X", &dhcp_universe, 210 },
+ { "option-211", "X", &dhcp_universe, 211 },
+ { "option-212", "X", &dhcp_universe, 212 },
+ { "option-213", "X", &dhcp_universe, 213 },
+ { "option-214", "X", &dhcp_universe, 214 },
+ { "option-215", "X", &dhcp_universe, 215 },
+ { "option-216", "X", &dhcp_universe, 216 },
+ { "option-217", "X", &dhcp_universe, 217 },
+ { "option-218", "X", &dhcp_universe, 218 },
+ { "option-219", "X", &dhcp_universe, 219 },
+ { "option-220", "X", &dhcp_universe, 220 },
+ { "option-221", "X", &dhcp_universe, 221 },
+ { "option-222", "X", &dhcp_universe, 222 },
+ { "option-223", "X", &dhcp_universe, 223 },
+ { "option-224", "X", &dhcp_universe, 224 },
+ { "option-225", "X", &dhcp_universe, 225 },
+ { "option-226", "X", &dhcp_universe, 226 },
+ { "option-227", "X", &dhcp_universe, 227 },
+ { "option-228", "X", &dhcp_universe, 228 },
+ { "option-229", "X", &dhcp_universe, 229 },
+ { "option-230", "X", &dhcp_universe, 230 },
+ { "option-231", "X", &dhcp_universe, 231 },
+ { "option-232", "X", &dhcp_universe, 232 },
+ { "option-233", "X", &dhcp_universe, 233 },
+ { "option-234", "X", &dhcp_universe, 234 },
+ { "option-235", "X", &dhcp_universe, 235 },
+ { "option-236", "X", &dhcp_universe, 236 },
+ { "option-237", "X", &dhcp_universe, 237 },
+ { "option-238", "X", &dhcp_universe, 238 },
+ { "option-239", "X", &dhcp_universe, 239 },
+ { "option-240", "X", &dhcp_universe, 240 },
+ { "option-241", "X", &dhcp_universe, 241 },
+ { "option-242", "X", &dhcp_universe, 242 },
+ { "option-243", "X", &dhcp_universe, 243 },
+ { "option-244", "X", &dhcp_universe, 244 },
+ { "option-245", "X", &dhcp_universe, 245 },
+ { "option-246", "X", &dhcp_universe, 246 },
+ { "option-247", "X", &dhcp_universe, 247 },
+ { "option-248", "X", &dhcp_universe, 248 },
+ { "option-249", "X", &dhcp_universe, 249 },
+ { "option-250", "X", &dhcp_universe, 250 },
+ { "option-251", "X", &dhcp_universe, 251 },
+ { "option-252", "X", &dhcp_universe, 252 },
+ { "option-253", "X", &dhcp_universe, 253 },
+ { "option-254", "X", &dhcp_universe, 254 },
+ { "option-end", "e", &dhcp_universe, 255 },
+};
+
+/*
+ * Default dhcp option priority list (this is ad hoc and should not be
+ * mistaken for a carefully crafted and optimized list).
+ */
+unsigned char dhcp_option_default_priority_list[] = {
+ DHO_DHCP_REQUESTED_ADDRESS,
+ DHO_DHCP_OPTION_OVERLOAD,
+ DHO_DHCP_MAX_MESSAGE_SIZE,
+ DHO_DHCP_RENEWAL_TIME,
+ DHO_DHCP_REBINDING_TIME,
+ DHO_DHCP_CLASS_IDENTIFIER,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ DHO_SUBNET_MASK,
+ DHO_TIME_OFFSET,
+ DHO_ROUTERS,
+ DHO_TIME_SERVERS,
+ DHO_NAME_SERVERS,
+ DHO_DOMAIN_NAME_SERVERS,
+ DHO_HOST_NAME,
+ DHO_LOG_SERVERS,
+ DHO_COOKIE_SERVERS,
+ DHO_LPR_SERVERS,
+ DHO_IMPRESS_SERVERS,
+ DHO_RESOURCE_LOCATION_SERVERS,
+ DHO_HOST_NAME,
+ DHO_BOOT_SIZE,
+ DHO_MERIT_DUMP,
+ DHO_DOMAIN_NAME,
+ DHO_SWAP_SERVER,
+ DHO_ROOT_PATH,
+ DHO_EXTENSIONS_PATH,
+ DHO_IP_FORWARDING,
+ DHO_NON_LOCAL_SOURCE_ROUTING,
+ DHO_POLICY_FILTER,
+ DHO_MAX_DGRAM_REASSEMBLY,
+ DHO_DEFAULT_IP_TTL,
+ DHO_PATH_MTU_AGING_TIMEOUT,
+ DHO_PATH_MTU_PLATEAU_TABLE,
+ DHO_INTERFACE_MTU,
+ DHO_ALL_SUBNETS_LOCAL,
+ DHO_BROADCAST_ADDRESS,
+ DHO_PERFORM_MASK_DISCOVERY,
+ DHO_MASK_SUPPLIER,
+ DHO_ROUTER_DISCOVERY,
+ DHO_ROUTER_SOLICITATION_ADDRESS,
+ DHO_STATIC_ROUTES,
+ DHO_TRAILER_ENCAPSULATION,
+ DHO_ARP_CACHE_TIMEOUT,
+ DHO_IEEE802_3_ENCAPSULATION,
+ DHO_DEFAULT_TCP_TTL,
+ DHO_TCP_KEEPALIVE_INTERVAL,
+ DHO_TCP_KEEPALIVE_GARBAGE,
+ DHO_NIS_DOMAIN,
+ DHO_NIS_SERVERS,
+ DHO_NTP_SERVERS,
+ DHO_VENDOR_ENCAPSULATED_OPTIONS,
+ DHO_NETBIOS_NAME_SERVERS,
+ DHO_NETBIOS_DD_SERVER,
+ DHO_NETBIOS_NODE_TYPE,
+ DHO_NETBIOS_SCOPE,
+ DHO_FONT_SERVERS,
+ DHO_X_DISPLAY_MANAGER,
+ DHO_DHCP_PARAMETER_REQUEST_LIST,
+
+ /* Presently-undefined options... */
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254,
+};
+
+int sizeof_dhcp_option_default_priority_list =
+ sizeof(dhcp_option_default_priority_list);
+
+struct hash_table universe_hash;
+
+void
+initialize_universes(void)
+{
+ int i;
+
+ dhcp_universe.name = "dhcp";
+ dhcp_universe.hash = new_hash();
+ if (!dhcp_universe.hash)
+ error("Can't allocate dhcp option hash table.");
+ for (i = 0; i < 256; i++) {
+ dhcp_universe.options[i] = &dhcp_options[i];
+ add_hash(dhcp_universe.hash,
+ (unsigned char *)dhcp_options[i].name, 0,
+ (unsigned char *)&dhcp_options[i]);
+ }
+ universe_hash.hash_count = DEFAULT_HASH_SIZE;
+ add_hash(&universe_hash,
+ (unsigned char *)dhcp_universe.name, 0,
+ (unsigned char *)&dhcp_universe);
+}
diff --git a/sbin/dhclient/tree.c b/sbin/dhclient/tree.c
new file mode 100644
index 000000000000..89ac450ac5ba
--- /dev/null
+++ b/sbin/dhclient/tree.c
@@ -0,0 +1,56 @@
+/* $OpenBSD: tree.c,v 1.13 2004/05/06 22:29:15 deraadt Exp $ */
+
+/* Routines for manipulating parse trees... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+
+extern int h_errno;
+
+pair
+cons(caddr_t car, pair cdr)
+{
+ pair foo = calloc(1, sizeof(*foo));
+ if (!foo)
+ error("no memory for cons.");
+ foo->car = car;
+ foo->cdr = cdr;
+ return (foo);
+}
diff --git a/sbin/dhclient/tree.h b/sbin/dhclient/tree.h
new file mode 100644
index 000000000000..04e08e7c820c
--- /dev/null
+++ b/sbin/dhclient/tree.h
@@ -0,0 +1,66 @@
+/* $OpenBSD: tree.h,v 1.5 2004/05/06 22:29:15 deraadt Exp $ */
+
+/* Definitions for address trees... */
+
+/*
+ * Copyright (c) 1995 The Internet Software Consortium. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+/* A pair of pointers, suitable for making a linked list. */
+typedef struct _pair {
+ caddr_t car;
+ struct _pair *cdr;
+} *pair;
+
+struct tree_cache {
+ unsigned char *value;
+ int len;
+ int buf_size;
+ time_t timeout;
+};
+
+struct universe {
+ char *name;
+ struct hash_table *hash;
+ struct option *options[256];
+};
+
+struct option {
+ char *name;
+ char *format;
+ struct universe *universe;
+ unsigned char code;
+};